Lesson 67Classes
Type Casting
What is Type Casting?
Type casting lets you check an instance's type at runtime and treat it as a different class type within its hierarchy. Essential for working with class inheritance!
Type Casting Operators
is
Check if instance is of a certain type
if item is Movie { }as?
Optional downcast (safe)
let m = item as? Movieas!
Forced downcast (crashes if wrong)
let m = item as! Movieas
Upcast to parent type
let media = movie as MediaItemAny and AnyObject
Any - Can represent any type (value or reference)
AnyObject - Can represent any class type only
Best Practices
- Prefer
as?overas!for safety - Use
isfor type checking without casting - Avoid
Anywhen possible - use generics instead
main.swift
// TYPE CASTING
// Check and convert between types at runtime
// CLASS HIERARCHY FOR EXAMPLES
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
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)
}
}
// MIXED ARRAY
let library: [MediaItem] = [
Movie(name: "Inception", director: "Christopher Nolan"),
Song(name: "Bohemian Rhapsody", artist: "Queen"),
Movie(name: "The Matrix", director: "Wachowskis"),
Song(name: "Imagine", artist: "John Lennon"),
Song(name: "Yesterday", artist: "Beatles")
]
// TYPE CHECKING WITH 'is'
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Movies: \(movieCount), Songs: \(songCount)")
// Movies: 2, Songs: 3
// DOWNCASTING WITH 'as?' (Optional)
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), Director: \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), Artist: \(song.artist)")
}
}
// FORCED DOWNCASTING WITH 'as!'
// Use only when you're CERTAIN of the type!
let firstItem = library[0]
let movie = firstItem as! Movie // We know index 0 is a Movie
print("\(movie.name) by \(movie.director)")
// UPCASTING WITH 'as'
let inception = Movie(name: "Inception", director: "Nolan")
let media: MediaItem = inception as MediaItem // Always succeeds
print(media.name)
// TYPE CASTING WITH ANY AND ANYOBJECT
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)" })
for thing in things {
switch thing {
case 0 as Int:
print("zero as Int")
case 0 as Double:
print("zero as Double")
case let someInt as Int:
print("an integer: \(someInt)")
case let someDouble as Double:
print("a double: \(someDouble)")
case let someString as String:
print("a string: \(someString)")
case let (x, y) as (Double, Double):
print("a point at \(x), \(y)")
case let movie as Movie:
print("movie: \(movie.name)")
case let stringConverter as (String) -> String:
print(stringConverter("World"))
default:
print("something else")
}
}
// PRACTICAL EXAMPLE: JSON-LIKE PARSING
func processData(_ data: Any) {
if let dict = data as? [String: Any] {
if let name = dict["name"] as? String {
print("Name: \(name)")
}
if let age = dict["age"] as? Int {
print("Age: \(age)")
}
}
}
let userData: [String: Any] = ["name": "Alice", "age": 25, "active": true]
processData(userData)Try It Yourself!
Create a Vehicle hierarchy with Car and Bike subclasses. Make an array and use type casting to print details about each!