Bummer! This is just a preview. You need to be signed in with a Basic account to view the entire video.
Start a free Basic trial
to watch this video
We may not always know the type of the objects that we’re working because they may be represented by a generic base class. In this video we take a look at how we can verify the type of a class as well as how to convert it to a more specific type.
-
0:00
Let's take a step away from our project and open up a new playground for a second.
-
0:05
In here, I have some code you can grab this code as well using
-
0:08
code snippet link in the teacher's notes of this video.
-
0:12
Go ahead and paste that into a playground in your end.
-
0:15
We start with a base class here, Employee, that contains just a name.
-
0:21
HourlyEmployee inherits from employee and adds information like hourly wages.
-
0:26
Similarly, salaried employee also inherits from employee and adds a salary property.
-
0:33
At the bottom here we have an array of employees.
-
0:36
We know from learning about arrays that Swift
-
0:39
arrays can only contain a single type.
-
0:42
But as you could see here we have two types, salaried and hourly employee,
-
0:46
both in the same array.
-
0:49
How is that possible?
-
0:50
Well, both these types have a common base type and when we mix and
-
0:55
match them in the array, Swift converts or
-
0:58
casts each object to the base type to satisfy the array homogeneity rule.
-
1:03
Meaning, they have to be the same thing in that array.
-
1:05
So if you check the type of the array, employees,
-
1:08
you'll see it's an array of employee rather than any of the specific subtypes.
-
1:14
It can only do this because they both share the same base class.
-
1:18
Let's add some code to each of our subclasses.
-
1:21
So over here now, you'll see that HourlyEmployee has a payWages method
-
1:27
that simply returns the wage times the hours work.
-
1:30
And the SalariedEmployee has a paySalary method.
-
1:33
So each of these has their own distinct pay method.
-
1:36
Now let's say we want to iterate through the items through the employees in
-
1:41
this employees array and pay each employee.
-
1:45
So we could say for employee, in employees and
-
1:51
then you'll notice that if we try to call either pay wages or pay salary,
-
1:57
none of those methods appear in the autocomplete and they just don't work.
-
2:03
This is because the type of each item that we get out of the array
-
2:07
is neither hourly nor salaried employee but the base class employee.
-
2:12
If you click on here, it says this is an instance of employee and
-
2:16
you'll notice that the class employee does not have any pay method so
-
2:19
we can't call anything on it.
-
2:22
At this point, the compiler does not know about these pay methods because it
-
2:26
thinks this is an Employee class.
-
2:29
To actually use this instance in the right context, we can either check but
-
2:34
it is the right type that we need or downcast them to a different type.
-
2:39
And to do that we have two operators at our disposal.
-
2:43
Let me get rid of this line of code.
-
2:46
The first one is the is operator.
-
2:49
The is operator returns true if the type matches a type we specify and
-
2:54
false if not.
-
2:56
So we can use it to inspect each item from the array.
-
2:59
So I can say, if employee, which is what we're pulling out of the array, the first
-
3:03
instance, is an instance of hourly employee, then go ahead and print hourly.
-
3:12
And you'll see that this has been executed once because there are two instances
-
3:16
only one of which is an hourly employee.
-
3:19
And similarly, we can say if employee is salaried employee, then print salary.
-
3:28
And this should also be executed just once.
-
3:33
Even though the array thinks that these items are of the base classes type,
-
3:37
we can check to see if there are instances of the sub class.
-
3:42
Now we don't want to get into the details of how this is done exactly in
-
3:45
the background.
-
3:46
But just know that even though it's an employee base class,
-
3:48
it actually contains information about what it actually is.
-
3:53
But all this allows us to do is check it, right?
-
3:55
I still can't call pay wages on this instance or pay salary on this instance.
-
4:01
What if we want to actually work with the specific sub types?
-
4:04
For that, we have another operator,
-
4:07
the Typecast operator as employee in this example is
-
4:12
a higher less specific type than any of the subclasses.
-
4:18
The as operator allows you to cast
-
4:21
to one of the more specific subclasses in a process known as downcasting.
-
4:27
It's called downcasting because if you think of the object graph as a tree, with
-
4:31
the base type on top, and more specific types as nodes extending downwards.
-
4:38
Then when going from a base, to a subclass we go down the tree.
-
4:43
Downcasting may not always succeed.
-
4:45
So the typecast operator comes into two flavors, the conditional form as with
-
4:50
a question mark and the forced form, as with an exclamation point.
-
4:55
The forced form, like the forced unwrap operator,
-
4:58
should only be used when you know the downcast will succeed.
-
5:04
For example, in our code, we verified that an employee instance
-
5:08
is of a particular type by using the type check operator.
-
5:12
We know that this employee here is an instance of the subclass for sure.
-
5:18
So inside the if statement, we can use the Force operator to downcast the instance.
-
5:23
So we can say let hourlyEmployee = employee as HourlyEmployee.
-
5:32
And now this instance is of type hourly employee.
-
5:37
This is the specific sub-class that we want and we can call the right method.
-
5:42
So we can say hourlyEmployee.payWages for 10.00 hours.
-
5:50
Similarly, if we were to do the same thing in here, we could say it let
-
5:55
salariedEmployee = employee as, and we're forcecasting
-
6:00
because inside this if statement, we're sure that employee is a salaried employee.
-
6:06
And then once we've done that, we can easily call paySalary.
-
6:11
The forced downcast operator, like the forced unwrap operator,
-
6:16
should be treated carefully however.
-
6:19
Only do this if you're 110% sure the downcast will succeed,
-
6:23
otherwise your application will crash.
-
6:26
For a safer option,
-
6:27
you can use the conditional typecast operator as with a question mark.
-
6:32
This operator returns an optional value if the CAS succeeds or nil if it fails.
-
6:39
So over here instead of as with an exclamation point, you can say,
-
6:42
employee as with a question mark.
-
6:45
Then when we do this, this instance now is an optional instance of HourlyEmployee.
-
6:51
So to call the pay wages method we have two options.
-
6:54
First, we can use optional chaining and
-
6:57
use a question mark to chain that method to the optional instance.
-
7:01
Or much cleaner, we can combine this, check over here,
-
7:05
employee as HourlyEmployee, with either and if let or
-
7:09
a guard let statement to unwrap the resulting value at once.
-
7:15
So we can see if let hourlyEmployee and then in here we
-
7:20
can call the right method on the unwrapped instance.
-
7:27
And as expected,
-
7:28
it should work and you'll see that we're calling and paying the wages right there.
-
7:34
Okay, that was a quick sidebar on what ambiguous types and downcasting is.
-
7:40
Now that you know this, let's head back to our main project.
-
7:43
Okay, where were we?
-
7:44
So we go to dictionary out of the P-list, but
-
7:47
we can't return it because it is the wrong type.
-
7:51
And NS dictionary in Swift is more specifically represented as
-
7:55
NS object to any object.
-
7:58
NS Object is an objective C type that is the base class for
-
8:01
every object in the language.
-
8:04
Similarly, any object represents any class in Swift because they both represent very
-
8:10
high level types, they can be downcast to a more concrete type that we want.
-
8:16
For now, we only want to cast the keys to strings.
-
8:19
We're going to return the values as any object, and
-
8:23
we'll do this by adding another condition to our guard statement right here.
-
8:28
So rather than adding a new one, we'll say guard let dictionary = and then I'm gonna
-
8:33
say as, I'm gonna use a question mark here cuz I don't want to force unwrap it, and
-
8:38
I'll say or forcecast in, then we'll say as string to any object.
-
8:45
So here we're using the conditional downcast operator to try and
-
8:49
cast the dictionary to the type we want.
-
8:53
If it works we have a dictionary cast to the right return type.
-
8:57
If not, we throw our conversion error.
-
9:00
And now we can finally return the dictionary.
You need to sign up for Treehouse in order to download course files.
Sign up