Reputation: 9342
Consider the following simplified protocol/class hierarchy
protocol P {
}
class A: P {
}
class B: P {
}
class C<T: P> {
}
I want to create a typed array of instances of the class C
. The automatic type inference does not seem to work, however. When I do
let objs = [C<A>(), C<B>()]
let obj = objs[0]
objs
and obj
are of type [AnyObject]
and AnyObject
, respectively. I would have expected something like
let objs:[C<P>] = [C<A>(), C<B>()]
to work but it doesn't compile with the error
Using 'P' as a concrete type conforming to protocol 'P' is not supported
Omitting the generic type altogether like so
let objs:[C] = [C<A>(), C<B>()]
produces a different error on compilation
Cannot convert value of type 'NSArray' to specified type '[C]'
Is there any way I could create an array of C
instances with a type more specific than [AnyObject]
?
Upvotes: 2
Views: 123
Reputation: 51911
Consider following code:
// DOES NOT WORK!!
protocol P {
var name: String { get }
}
class A: P {
var name = "A"
}
class B: P {
var name = "B"
}
class C<T: P> {
var val: T
init(val: T) {
self.val = val
}
}
let objs: [C<P>] = [ C<A>(A()) ]
let firstObj: C<P> = obj[0]
firstObj.val = B()
In this case, firstObj
is actually a C<A>
instance. But firstObj.val
must accept B()
because firstObj.val
is constrained to P
and B
conforms to P
. This is illegal you know. That is why you cannot cast C<A>
as C<P>
To workaround this, for example, you can create some wrapper around the C
:
protocol P {
var name: String { get }
}
class A: P {
var name = "A"
}
class B: P {
var name = "B"
}
class C<T: P> {
var val: T
init(_ val: T) {
self.val = val
}
}
/// Type erasing wrapper around C that has accessor for `C.val`
struct AnyC {
let _val: () -> P
var val: P { return _val() }
init<T>(_ c: C<T>) {
_val = { return c.val }
}
}
let objs:[AnyC] = [
AnyC( C<A>(A()) ),
AnyC( C<B>(B()) ),
]
objs[0].val.name // -> "A"
objs[1].val.name // -> "B"
Upvotes: 2