Type conversion

A cast can determine the type of an instance or treat the instance as an instance of its parent or subclass. Type conversions are implemented in Swift using the IS and AS operators. These operators provide an easy way to check the type of a value or convert it, respectively. You can also use it to check whether a type complies with a protocol, as described in the verifying protocol compliance section.


Define class hierarchies for type conversions

You can use type conversions on hierarchies of classes and subclasses, checking the type of a particular class instance and converting the type of that class instance to other types in the hierarchy. The following three code snippets define a class hierarchy and an array containing instances of these classes as examples of type conversions.

The first code snippet defines a new base class, MediaItem. This class provides basic functionality for any media item that appears in the digital media library. In particular, it declares a String name property and an init(name:) initializer. (Assume that all media items have a name.)

class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}
Copy the code

The next code snippet defines two subclasses of MediaItem. The first subclass, Movie, encapsulates additional movie-related information by adding a director attribute and an initializer to the parent (or base) class. The second subclass, Song, adds an artist attribute and an initializer to the parent class:

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)
    }
}
Copy the code

The last code snippet creates an array constant library containing two instances of Movie and three instances of Song. The type of library is inferred from the contents of its array when it is initialized. Swift’s type detector can infer that Movie and Song share a common parent class, MediaItem, so it infer that the [MediaItem] class is the type of 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 array library is inferred to be [MediaItem]
Copy the code

The media items stored in the behind-the-scenes library are still Movie and Song. However, if you iterate over it, the instances in turn will be MediaItem, not Movie and Song. To make them work as native types, you need to check their type or cast them down to another type, as described below.


Check the type

The type checking operator (IS) is used to check whether an instance belongs to a particular subtype. The type checking operator returns true if the instance belongs to that subtype, false otherwise.

The following example defines two variables, movieCount and songCount, to count the number of instances of the Movie and Song types in the array library:

var movieCount = 0
var songCount = 0

for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1}}print("Media library contains \(movieCount) movies and \(songCount) songs")
// Print "Media library contains 2 movies and 3 songs"
Copy the code

The example iterates through all items in the array library. Each time, the for-in loop sets the Item constant to the next MediaItem instance in the array. Item is Movie returns true if the current MediaItem is an instance of type Movie, false otherwise. Similarly, item is Song checks if item is an instance of type Song. At the end of the loop, the values of movieCount and songCount are the number of instances of their types found.


Downward transition

A constant or variable of a type may actually belong to a subclass behind the scenes. When you determine that this is the case, you can try using the type conversion operator (as? Or as! Scroll down to its subtype.

Because downcasting can fail, the type transformation operator comes in two different forms. The conditional form as? Returns an optional value of the type you are trying to cast down to. Enforces the form as! Combine the attempted downward transformation and forced unpacking of the transformation result into one operation.

When you are not sure that a downward cast will succeed, use the conditional form (as?) for type casting. . Conditional conversions always return an optional value, and the optional value will be nil if the downcast is not possible. This allows you to check if the downward transition is successful.

Use the mandatory form (as!) only if you can be sure that the downward transition will succeed. . When you try to cast down to an incorrect type, a forced type conversion will trigger a runtime error.

The following example iterates through each MediaItem in the library and prints out the appropriate description. To do this, item needs to really be used as a type of Movie or Song, not just as a MediaItem. This is necessary in order to be able to use the director or artist attribute of Movie or Song in the description.

In this example, each item in the array could be Movie or Song. You don’t know the true type of each item beforehand, so conditional conversions (as?) are used. To check each down turn in the loop:

for item in library {
    if let movie = item as? Movie {
        print("Movie: \(movie.name), dir. \(movie.director)")}else if let song = item as? 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
Copy the code

The example first tries to convert under item to Movie. Because item is an instance of the MediaItem type, it might be a Movie; Again, it could be a Song, or just the base class MediaItem. Because of the uncertainty, as? The form returns an optional value when attempting to roll down. item as? The return value of Movie is Movie? Or “optional Movie.”

The Movie application will fail when transitioning down to two Song instances. To deal with this, the above example uses an optional binding to check that the optional Movie actually contains a value (this is to determine if the roll-down was successful). The optional binding is written “if let movie = item as? Movie “, can be read as: “Try to convert item to Movie. If successful, set a new temporary constant movie to store the value from the returned optional movie.

If the downward transition succeeds, the movie properties are then used to print a description of the movie instance, including the name of its director. Similar principles are used to detect Song instances, and when a Song is found, print its description (including artist’s name).

Note that the transformation does not really change the instance or its value. The underlying instance remains the same; Simply use it as the type to which it was converted.


AnyandAnyObjectType conversion of

Swift provides two special type aliases for uncertain types:

Any can represent Any type, including function types.

AnyObject can represent an instance of any class type.

Use Any and AnyObjects only when you really need their behavior and functionality. It is best to specify the type you want to use in your code.

Here is an example of using the Any type to work with a mix of different types, both functional and non-class. It creates an array that can store things of type Any:

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) - >String in "Hello, \(name)" })
Copy the code

The Things array contains two ints, two doubles, a String, a tuple (Double, Double), a Movie instance “Ghostbusters”, And a closure expression that takes a String value and returns another String value.

You can use the IS and AS operators in the case of switch expressions to find specific types of constants or variables known only as Any or AnyObject. The following example iterates over each item in the THINGS array and uses the switch statement to find the type of each item. The case of several switch statements binds the value they match to a constant of the specified type, thus printing these values:

for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\ "")
    case let (x, y) as (Double.Double) :print("an (x, y) point at \(x).\(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (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
Copy the code

Pay attention to

The Any type can represent values of all types, including optional types. Swift warns you when you use Any to represent an optional value. If you really want to use Any to carry optional values, you can use the AS operator to explicitly convert to Any, as shown below:

let optionalNumber: Int? = 3
things.append(optionalNumber)        / / warning
things.append(optionalNumber as Any) // No warning
Copy the code