AnyCollection([1, 2, 3]) // AnyCollection<Int>
AnyCollection(["a", "b", "c"]) // AnyCollection<String>
These two AnyCollection
is great, but they have different Generic types, namely Int
and String
.
But I can still add these two AnyCollection
to a single array, like this
// [AnyCollection<Any>]
let array = [AnyCollection([1, 2, 3]), AnyCollection(["a", "b", "c"])]
I can't understand what happened to these two AnyCollection
.
Why can it convert from String
or Int
to Any
??
I wrote some code to create one myself.
// ❌ Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
// Contains AnyMyCollection<String> && AnyMyCollection<Int> at a single collection.
var collections = [AnyMyCollection(Sports()), AnyMyCollection(Animals()), AnyMyCollection(Digits())]
protocol MyCollection<Element> {
associatedtype Element
func allValues() -> [Element]
}
// MARK: - AnyMyCollection
struct AnyMyCollection<Element> {
internal var _box: _AnyMyCollectionBase<Element>
init<C: MyCollection>(_ base: C) where C.Element == Element {
self._box = _MyCollectionBox(base)
}
}
extension AnyMyCollection: MyCollection {
func allValues() -> [Element] {
_box.allValues()
}
}
final class _MyCollectionBox<Base: MyCollection>: _AnyMyCollectionBase<Base.Element> {
init(_ base: Base) {
_base = base
}
private var _base: Base
override func allValues() -> [Base.Element] {
_base.allValues()
}
}
class _AnyMyCollectionBase<Element>: MyCollection {
func allValues() -> [Element] {
return []
}
}
// MARK: - Diffrent Types of My Collection
struct Animals: MyCollection {
typealias Element = String
func allValues() -> [Element] {
["Monkey", "Tiger", "Lion"]
}
}
struct Sports: MyCollection {
typealias Element = String
func allValues() -> [Element] {
["Basketball", "Football", "Baseball"]
}
}
struct Digits: MyCollection {
typealias Element = Int
func allValues() -> [Element] {
[1, 2, 3, 4, 5]
}
}
I tried to follow the same technique but failed because the type of the element in AnyMyCollection
is not the same.
The simple explanation is that you are not Apple.
Generics in general are not covariant over the parameterized type, and you have no way to create to generic that is covariant over the paramterized type. But Apple does.
To see this more simply, consider Array. It is a generic. If you have two classes, a class and its subclass, you can combine arrays of each of them:
class MyClass {}
class MySubclass: MyClass {}
let arr1 = [MyClass()]
let arr2 = [MySubclass()]
let arr3 = [arr1, arr2] // [[MyClass]]
The compiler accepts this; it treats the resulting combination as an array of arrays of the superclass, Array<Array<MyClass>>
. So for Apple, an Array<MySubclass>
is treated as a sort of subclass of Array<MyClass>
.
But now try to do that with your own generic. You can't do it:
class MyGeneric<T> {}
let g1 = MyGeneric<MyClass>()
let g2 = MyGeneric<MySubclass>()
let arr4 = [g1, g2] // error
The compiler does not magically see this as an Array<MyGeneric<MyClass>>
. That's because your generic is not covariant.
There are always language proposals sitting around, hoping to allow us ordinary human beings to make covariant generics. But so far, none of them has arrived into the language.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.