Why must I downcast an array item in Swift, if the sub-class of the array item is known?
> class B {let t: String = "-B-"}
> class B1:B {let t1: String = "-B1-"}
> class B2:B {let t2: String = "-B2-"}
> let bunch = [B(), B1(), B2()]
As expected:
> print(type(of:bunch))
Array<B>
The sub-class of each element of the array is known:
> for item in bunch {print(type(of:item))}
B
B1
B2
Yet (as documented) we cannot access the item's sub-class member, and we have to down-class:
> if bunch[1] is B1 {let b1 = bunch[1] as! B1; print(b1.t1)}
-B1-
because this does not work:
> bunch[1].t1
error: repl.swift:17:6: error: value of type 'B' has no member 't1'; did you mean 't'?
Why can Swift determine the sub-class using type(of:) but it cannot infer it when accessing a member in that sub-class? Is it a bug, a historic hang-over, or am I missing something?
Swift arrays are collections of items of a single type. The type of the array
let bunch = [B(), B1(), B2()]
is inferred as [B]
because B
is the “nearest common superclass” of the given three elements. Consequently, bunch[1]
has the type B
and not B1
. That is why bunch[1].t1
does not compile.
Of course you can print the actual type of an array element at runtime with type(of:)
, or check it against B1
. The latter is better done with optional binding:
if let b1 = bunch[1] as? B1 {
print(b1.t1)
}