Type Casting (类型转换)

作者: 金旭峰 | 来源:发表于2017-01-21 09:43 被阅读24次

    Type castingis a way to check the type of an instance, or to treat that instance as a different superclass or subclass from somewhere else in its own class hierarchy.

    类型转换可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。

    Type casting in Swift is implemented with theisandasoperators. These two operators provide a simple and expressive way to check the type of a value or cast a value to a different type.

    类型转换在 Swift 中使用is和as操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。

    You can also use type casting to check whether a type conforms to a protocol, as described inChecking for Protocol Conformance.

    你也可以用它来检查一个类型是否实现了某个协议,就像在检验协议的一致性部分讲述的一样。

    Defining a Class Hierarchy for Type Casting (定义一个类层次作为例子)

    You can use type casting with a hierarchy of classes and subclasses to check the type of a particular class instance and to cast that instance to another class within the same hierarchy. The three code snippets below define a hierarchy of classes and an array containing instances of those classes, for use in an example of type casting.

    你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了这些类实例的数组,作为类型转换的例子。

    The first snippet defines a new base class calledMediaItem. This class provides basic functionality for any kind of item that appears in a digital media library. Specifically, it declares anameproperty of typeString, and aninit nameinitializer. (It is assumed that all media items, including all movies and songs, will have a name.)

    第一个代码片段定义了一个新的基类MediaItem。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个String类型的name属性,和一个init(name:)初始化器。(假定所有的媒体项都有个名称。)

    class MediaItem{

        var name:String

        init(name:String) {

        self.name=name

        }

    }

    The next snippet defines two subclasses ofMediaItem. The first subclass,Movie, encapsulates additional information about a movie or film. It adds adirectorproperty on top of the baseMediaItemclass, with a corresponding initializer. The second subclass,Song, adds anartistproperty and initializer on top of the base class:

    下一个代码段定义了MediaItem的两个子类。第一个子类Movie封装了与电影相关的额外信息,在父类(或者说基类)的基础上增加了一个director(导演)属性,和相应的初始化器。第二个子类Song,在父类的基础上增加了一个artist(艺术家)属性,和相应的初始化器:

    class Movie:MediaItem{

        var director:String

        init(name:String,director:String) { 

            self.director=director

            super.init(name:name)

        }

    }

    class Song:MediaItem{

        var artist:String

        init(name:String,artist:String) {

            self.artist=artist

            super.init(name:name)

        }

    }

    The final snippet creates a constant array calledlibrary, which contains twoMovieinstances and threeSonginstances. The type of thelibraryarray is inferred by initializing it with the contents of an array literal. Swift’s type checker is able to deduce thatMovieandSonghave a common superclass ofMediaItem, and so it infers a type of[MediaItem]for thelibraryarray:

    最后一个代码段创建了一个数组常量library,包含两个Movie实例和三个Song实例。library的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift 的类型检测器能够推断出Movie和Song有共同的父类MediaItem,所以它推断出[MediaItem]类作为library的类型:

    let library= [

       Movie(name:"Casablanca",director:"Michael Curtiz"),

        Song(name:"Blue Suede Shoes",artist:"Elvis Presley"),

        Movie(name:"Citizen Kane",director:"Orson Welles"),

        Song(name:"The One And Only",artist:"Chesney Hawkes"),

        Song(name:"Never Gonna Give You Up",artist:"Rick Astley")

    ]

    // the type of "library" is inferred to be [MediaItem]

    The items stored inlibraryare stillMovieandSonginstances behind the scenes. However, if you iterate over the contents of this array, the items you receive back are typed asMediaItem, and not asMovieorSong. In order to work with them as their native type, you need tochecktheir type, ordowncastthem to a different type, as described below.

    在幕后library里存储的媒体项依然是Movie和Song类型的。但是,若你迭代它,依次取出的实例会是MediaItem类型的,而不是Movie和Song类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。

    Checking Type (检查类型)

    Use thetype check operator(is) to check whether an instance is of a certain subclass type. The type check operator returnstrueif the instance is of that subclass type andfalseif it is not.

    用类型检查操作符(is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回true,否则返回false。

    The example below defines two variables,movieCountandsongCount, which count the number ofMovieandSonginstances in thelibraryarray:

    下面的例子定义了两个变量,movieCount和songCount,用来计算数组library中Movie和Song类型的实例数量:

    var movieCount=0

    var songCount=0

    for item in library{

        if itemisMovie{

            movieCount+=1

        } else if itemisSong{

            songCount+=1

        }

    }

    print("Media library contains\(movieCount)movies and\(songCount)songs")

    // Prints "Media library contains 2 movies and 3 songs"

    This example iterates through all items in thelibraryarray. On each pass, thefor-inloop sets theitemconstant to the nextMediaItemin the array.

    示例迭代了数组library中的所有项。每一次,for-in循环设置item为数组中的下一个MediaItem。

    item is Moviereturnstrueif the currentMediaItemis aMovieinstance andfalseif it is not. Similarly,item is Songchecks whether the item is aSonginstance. At the end of thefor-inloop, the values ofmovieCountandsongCountcontain a count of how manyMediaIteminstances were found of each type.

    若当前MediaItem是一个Movie类型的实例,item is Movie返回true,否则返回false。同样的,item is Song检查item是否为Song类型的实例。在循环结束后,movieCount和songCount的值就是被找到的属于各自类型的实例的数量。

    Downcasting (向下转型)

    A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try todowncastto the subclass type with atype cast operator(as?oras!).

    某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(as?或as!)。

    Because downcasting can fail, the type cast operator comes in two different forms. The conditional form,as?, returns an optional value of the type you are trying to downcast to. The forced form,as!, attempts the downcast and force-unwraps the result as a single compound action.

    因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式as?返回一个你试图向下转成的类型的可选值。强制形式as!把试图向下转型和强制解包(转换结果结合为一个操作。

    Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will benilif the downcast was not possible. This enables you to check for a successful downcast.

    当你不确定向下转型可以成功时,用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值,并且若下转是不可能的,可选值将是nil。这使你能够检查向下转型是否成功。

    Use the forced form of the type cast operator (as!) only when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type.

    只有你可以确定向下转型一定会成功时,才使用强制形式(as!)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。

    The example below iterates over eachMediaIteminlibrary, and prints an appropriate description for each item. To do this, it needs to access each item as a trueMovieorSong, and not just as aMediaItem. This is necessary in order for it to be able to access thedirectororartistproperty of aMovieorSongfor use in the description.

    下面的例子,迭代了library里的每一个MediaItem,并打印出适当的描述。要这样做,item需要真正作为Movie或Song的类型来使用,而不仅仅是作为MediaItem。为了能够在描述中使用Movie或Song的director或artist属性,这是必要的。

    In this example, each item in the array might be aMovie, or it might be aSong. You don’t know in advance which actual class to use for each item, and so it is appropriate to use the conditional form of the type cast operator (as?) to check the downcast each time through the loop:

    在这个示例中,数组中的每一个item可能是Movie或Song。事前你不知道每个item的真实类型,所以这里使用条件形式的类型转换(as?)去检查循环里的每次下转:

    for item in library{

        if let movie=itemas?Movie{

            print("Movie:\(movie.name), dir.\(movie.director)")

        }else if let song=itemas?Song{

            print("Song:\(song.name), by\(song.artist)")

        }

    }

    // Movie: Casablanca, dir. Michael Curtiz

    // Song: Blue Suede Shoes, by Elvis Presley

    // Movie: Citizen Kane, dir. Orson Welles

    // Song: The One And Only, by Chesney Hawkes

    // Song: Never Gonna Give You Up, by Rick Astley

    The example starts by trying to downcast the currentitemas aMovie. Becauseitemis aMediaIteminstance, it’s possible that itmightbe aMovie; equally, it’s also possible that it might be aSong, or even just a baseMediaItem. Because of this uncertainty, theas?form of the type cast operator returns anoptionalvalue when attempting to downcast to a subclass type. The result ofitem as? Movieis of typeMovie?, or “optionalMovie”.

    示例首先试图将item下转为Movie。因为item是一个MediaItem类型的实例,它可能是一个Movie;同样,它也可能是一个Song,或者仅仅是基类MediaItem。因为不确定,as?形式在试图下转时将返回一个可选值。item as? Movie的返回值是Movie?或者说“可选Movie”。

    Downcasting toMoviefails when applied to theSonginstances in the library array. To cope with this, the example above uses optional binding to check whether the optionalMovieactually contains a value (that is, to find out whether the downcast succeeded.) This optional binding is written “if let movie = item as? Movie”, which can be read as:

    当向下转型为Movie应用在两个Song实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选Movie真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“if let movie = item as? Movie”,可以这样解读:

    “Try to accessitemas aMovie. If this is successful, set a new temporary constant calledmovieto the value stored in the returned optionalMovie.”

    “尝试将item转为Movie类型。若成功,设置一个新的临时常量movie来存储返回的可选Movie中的值”

    If the downcasting succeeds, the properties ofmovieare then used to print a description for thatMovieinstance, including the name of itsdirector. A similar principle is used to check forSonginstances, and to print an appropriate description (includingartistname) whenever aSongis found in the library.

    若向下转型成功,然后movie的属性将用于打印一个Movie实例的描述,包括它的导演的名字director。相似的原理被用来检测Song实例,当Song被找到时则打印它的描述(包含artist的名字)。

    NOTE

    Casting does not actually modify the instance or change its values. The underlying instance remains the same; it is simply treated and accessed as an instance of the type to which it has been cast.

    转换没有真的改变实例或它的值。根本的实例保持不变;只是简单地把它作为它被转换成的类型来使用。

    Type Casting for Any and AnyObject (Any和AnyObject的类型转换)

    Swift provides two special types for working with nonspecific types:

    Swift 为不确定类型提供了两种特殊的类型别名:

    1. Any can represent an instance of any type at all, including function types.

    Any可以表示任何类型,包括函数类型。

    2. AnyObject can represent an instance of any class type.

    AnyObject可以表示任何类类型的实例。

    UseAnyandAnyObjectonly when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code.

    只有当你确实需要它们的行为和功能时才使用Any和AnyObject。在你的代码里使用你期望的明确类型总是更好的。

    Here’s an example of usingAnyto work with a mix of different types, including function types and non-class types. The example creates an array calledthings, which can store values of typeAny:

    这里有个示例,使用Any类型来和混合的不同类型一起工作,包括函数类型和非类类型。它创建了一个可以存储Any类型的数组things:

    var things= [Any]()

    things.append(0)

    things.append(0.0)

    things.append(42)

    things.append(3.14159)

    things.append("hello")

    things.append((3.0,5.0))

    things.append(Movie(name:"Ghostbusters",director:"Ivan Reitman"))

    things.append({ (name:String) ->Stringin"Hello,\(name)"})

    Thethingsarray contains twoIntvalues, twoDoublevalues, aStringvalue, a tuple of type(Double, Double), the movie “Ghostbusters”, and a closure expression that takes aStringvalue and returns anotherStringvalue.

    things数组包含两个Int值,两个Double值,一个String值,一个元组(Double, Double),一个Movie实例“Ghostbusters”,以及一个接受String值并返回另一个String值的闭包表达式。

    To discover the specific type of a constant or variable that is known only to be of typeAnyorAnyObject, you can use anisoraspattern in aswitchstatement’s cases. The example below iterates over the items in thethingsarray and queries the type of each item with aswitchstatement. Several of theswitchstatement’s cases bind their matched value to a constant of the specified type to enable its value to be printed:

    你可以在switch表达式的case中使用is和as操作符来找出只知道是Any或AnyObject类型的常量或变量的具体类型。下面的示例迭代things数组中的每一项,并用switch语句查找每一项的类型。有几个switch语句的case绑定它们匹配到的值到一个指定类型的常量,从而可以打印这些值:

    for thing in things{

        switchthing{

            case0asInt:

               print("zero as an Int")

            case0asDouble:

               print("zero as a Double")

            case let someIntasInt:

                print("an integer value of\(someInt)")

            case let someDoubleasDouble where someDouble>0:

                print("a positive double value of\(someDouble)")

            case isDouble:

                print("some other double value that I don't want to print")

            case let someStringasString:

                print("a string value of \"\(someString)\"")

            case let (x,y)as(Double,Double):

                print("an (x, y) point at\(x),\(y)")

            case let movieasMovie:

                print("a movie called\(movie.name), dir.\(movie.director)")

            case let stringConverteras(String) ->String:

                print(stringConverter("Michael"))

            default:

                print("something else")

        }

    }

    // zero as an Int

    // zero as a Double

    // an integer value of 42

    // a positive double value of 3.14159

    // a string value of "hello"

    // an (x, y) point at 3.0, 5.0

    // a movie called Ghostbusters, dir. Ivan Reitman

    // Hello, Michael

    NOTE

    TheAnytype represents values of any type, including optional types. Swift gives you a warning if you use an optional value where a value of typeAnyis expected. If you really do need to use an optional value as anAnyvalue, you can use theasoperator to explicitly cast the optional toAny, as shown below.

    Any类型可以表示所有类型的值,包括可选类型。Swift 会在你用Any类型来表示一个可选值的时候,给你一个警告。如果你确实想使用Any类型来承载可选值,你可以使用as操作符显示转换为Any,如下所示:

    let optionalNumber:Int? =3

    things.append(optionalNumber)// Warning

    things.append(optionalNumberasAny)// No warning

    相关文章

      网友评论

        本文标题:Type Casting (类型转换)

        本文链接:https://www.haomeiwen.com/subject/fuigbttx.html