Trusted answers to developer questions
Trusted Answers to Developer Questions

Related Tags

swift

What is up and down casting in Swift?

Tahreem Yasir

Grokking Modern System Design Interview for Engineers & Managers

Ace your System Design Interview and take your career to the next level. Learn to handle the design of applications like Netflix, Quora, Facebook, Uber, and many more in a 45-min interview. Learn the RESHADED framework for architecting web-scale applications by determining requirements, constraints, and assumptions before diving into a step-by-step design process.

Overview

Swift uses up and down casting to convert instances of one class type to its sub or superclass within the same hierarchy.

Note: The up and down casting can only be performed within the same hierarchies to sub or superclasses.

Upcasting

Upcasting is changing the type of an instance of a subclass into its superclass. We can think of it as moving up within the same hierarchy.

Class diagram

Let's consider the following illustration where we have different classes arranged in a hierarchy, where MediaItem is the parent class while Movie Bookand Songare its child classes. We'll use the following example throughout while discussing up and down casting:

%0 node_1 MediaItem node_2 Movie node_1->node_2 node_3 Song node_1->node_3 node_4 Book node_1->node_4
The Media class and its subclasses

Class definitions

The code snippet below shows the respective class definitions, where class MediaItem only contains the name of the media item, while its subclasses comprise other details such as names of directors, authors, or artists.

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)
}
}
class Book: MediaItem {
var author: String
init(name: String, author: String) {
self.author = author
super.init(name: name)
}
}
Definition of classes

Why should we use upcasting

We already know that Swift allows upcasting, which means subclasses can be declared or treated as their parent class during their lifetime. It is explained with the help of the following illustration:

Supercasting - subclasses can be declared or changed to superclass

Code example

In this example, we'll try to establish why upcasting is necessary for object-oriented paradigms and what its benefits are.

In the following code snippet, an array named library has been initialized that contains two movies, two books, and three songs. The type checker of Swift can deduce that the objects stored in the library have the same superclass, so it converts the type of library to MediaItem.

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"),
Book(name: "Wuthering Heights", author: "Emily Bronte"),
Book(name: "Becoming", author: "Michelle Obama")
]
Array conatining instances of Movie, Songs and Books

To process the library array, no explicit typecasting is needed because the type of library has been converted to MediaItem by the compiler at runtime. We can check the data type of library using the following method:

let t = type(of:library)
print("The type of library is \(t)")
Printing the type of array library

The is operator

To check the actual data types of elements inside the library array, we can use the is operator. The is operator returns True if the item belongs to some class and False otherwise.

var movieCount = 0
var songCount = 0
var bookCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}else if item is Book {
bookCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs and \(bookCount) Books.")
// Prints "Media library contains 2 movies and 3 songs and 2 books"
Counting objects of subclass type in the Library array using `is` Operator

Downcasting

Downcasting is used to reconvert the object of superclass back to their subclasses. We can think of it as moving down the hierarchy. Like upcasting down casting can only be done within the same hierarchies.

Why we should use downcasting

Although the default type of library is MediaItem, but all the elements do not belong to the same class. Therefore, when we reconvert each element into its respective subclass, it may result in an error as downcasting to a single class is not possible.

That is why we have to make sure that each element of library should be converted into its respective subclass type. The concept is illustrated from the following hierarchy where the superclass is being downcasted to its subclasses:

Downcasting - superclass can be declatred or changed to subclasses

The as? operator

To convert each element of the library into its respective time, we can use an operator named as? . This operator is used for optional downcasting and can be used when it is unsure if the downcast will succeed.

In the following code snippet, the first line accesses an element from library and changes it into an object of type Song. If the conversion is successful, library[0] will be saved in song, otherwise song will point to nil.

As we know that the first element of library is not a song. Therefore, the downcast will be failed. To downcast the library array, conditional statements can be used along with optional downcasting as? to convert each element of the array to its respective subclass type.

let song = library[0] as? Song
if song != nil
{
print("Downcast is successful")
} else {
print("song points to nil, downcast is unsuccessful")
}
Optional downcasting

The as! operator

For the cases where we exactly know the types of subclasses, Swift has an as! operator. This operator is used for forced downcasting, where it is known that downcasting will succeed. As we know that the data type of the second element of library the array, that is, library[1] is Song so in the following code example, we use forced conversion to subclass type.

let song = library[1] as! Song
print("Song is \(song.name), artist is \(song.artist).")
Forced downcasting

RELATED TAGS

swift

CONTRIBUTOR

Tahreem Yasir
Copyright ©2022 Educative, Inc. All rights reserved

Grokking Modern System Design Interview for Engineers & Managers

Ace your System Design Interview and take your career to the next level. Learn to handle the design of applications like Netflix, Quora, Facebook, Uber, and many more in a 45-min interview. Learn the RESHADED framework for architecting web-scale applications by determining requirements, constraints, and assumptions before diving into a step-by-step design process.

Keep Exploring