hennes
hennes

Reputation: 9342

Typed array of generic classes

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

Answers (1)

rintaro
rintaro

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

Related Questions