Type casting in swift : difference between is, as, as?, as!
What is the difference between is, as, as?, as! in swift? Well, lets check.
--
Apple doc says: Type casting is 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 the
is
andas
operators.is
is used to check the type of a value whereasas
is used to cast a value to a different type.
Consider the following classs LivingBeing
and two subclasses of LivingBeing
named Human
and Animal
.
Now create a constant array called livingBeingArray
with one Animal
class object and one human
class object. What do you think the type of this array created via type inference? It will be of type [LivingBeing]
.
Swift’s type checker is able to deduce that
Human
andAnimal
have a common superclass ofLivingBeing
, and so it infers a type of[LivingBeing]
for thelivingBeingArray
array.
The items stored in livingBeingArray
are still Human
and Animal
instances behind the scenes. However, if you iterate over the contents of this array, the items you receive back are typed as LivingBeing
, and not as Human
or Animal
. In order to work with them as their native type, you need to check their type, or downcast them to a different type.
Checking Type
Use the type check operator (is
) to check whether an instance is of a certain subclass type. The type check operator returns true
if the instance is of that subclass type and false
if it is not.
Consider the following code:
let livingBeingObj = livingBeingArray[0] // returns a LivingBeing object.
Let’s iterate the array objects over a for
loop.
for item in livingBeingArray {if item is Animal {print("item is of type Animal")// will get executed for first item} else if item is Human {print("item is of type Human")// will get executed for second item}}
Downcasting
Apple doc says: 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 to downcast to the subclass type with a type cast operator (
as?
oras!
).
Let’s simplify this. Consider the array livingBeingArray
. We know that the first item is of type Animal
. Since array contains one Animal
object and one Human
object, the type inference will decide the array type as LivingBeing
. If we try to get any content from this array, it will return you an object of type LivingBeing.
In that case we can try to downcast it after fetching it form the array.
Difference between as? and as!
Downcasting can be done in two ways:
- Conditional downcasting (as?).
- Forced downcasting (as!).
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.
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 be nil
if the downcast was not possible. This enables you to check for a successful downcast.
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.
Here in the above scenario, since we know that the first object in the array is of type Animal, we can use Forced downcasting.
let animalObj = livingBeingArray[0] as! Animal //forced downcasting to Animallet humanObj = livingBeingArray[1] as! Human //forced downcasting to Human
But forced downcasting can fail if we try to downcast the first object to a Human
and second object to an Animal
. In this case the result will be nil
which a normal type cannot handle and the program will crash.
let animalObj = livingBeingArray[0] as! Human //error and crasheslet humanObj = livingBeingArray[1] as! Animal //error and crashes
In this scenario, where we are not sure if the casting succeeds, we should use the conditional downcasting as?
.
let animalObj = livingBeingArray[0] as? Human //nil..animalObj is of Human? (optional Human which is the type which we tried to downcast to)let humanObj = livingBeingArray[1] as? Animal //nil..humanObj is of Animal? (optional Animal which is the type which we tried to downcast to)
But conditional downcasting of the correct type succeeds and returns the correct optional type that we are trying to downcast to.
let animalObj = livingBeingArray[0] as? Animal // success, returns Animal?let humanObj = livingBeingArray[1] as? Human // success, returns Human?
Upcasting
Upcasting from the base class object to its superclass is also possible. Let’s convert the animalObject
created by forced downcasting back to the LivingBeing
class.
let animalObj = livingBeingArray[0] as! Animal
let animalObjectAsLivingBeingObj = animalObj as LivingBeing
animalObjectAsLivingBeingObj
is of type LivingBeing
.
Type Casting for Any and AnyObject
Swift provides two special types for working with nonspecific types:
Any
can represent an instance of any type at all, including function types.AnyObject
can represent an instance of any class type.
The keyword ‘Any’ is used to represent an instance which belongs to any type including function types. Consider an array of type Any
which can accept different types of values. We can use a switch statement to checl the type and do downcasting.
var groups = [Any]()
groups.append(1.0)
groups.append(1)
groups.append("string")
for item in groups {
switch item {
case let anInt as Int:
print("\(item) is an int")
case let aDouble as Double:
print("\(item) is a double")
case let aString as String:
print("\(item) is a string")
default:
print("dunno")
}
}
/*
1.0 is a double
1 is an int
string is a string
C11lldb_expr_13Pop (has 1 child) is a Genre
*/
Forced and conditional downcasting will not work in a
switch-case
Using switch statement for type checking and downcasting :
for obj in livingBeingArray
{
switch obj {
case let animalObj as Animal:
print(“\(obj) is an animal”)
break
case let humanObj as Human:
print(“\(obj) is an human”)
break
default:
print(“unknown type”)
}
}
you may also use is operator for type checking.
for obj in livingBeingArray
{
switch obj {
case is Animal:
print(“\(obj) is an animal”)
break
case is Human:
print(“\(obj) is an human”)
break
default:
print(“unknown type”)
}
}
Source: AppleDocs
Enjoy!!