Reputation: 5741
I have 2 classes, both are subclass of SuperClass
:
class A: SuperClass {
...
static let arrayA: [A] = [a, b, c]
}
class B: SuperClass {
...
static let arrayB: [B] = [1, 2, 3]
}
Then I want to create a new array of type SuperClass
:
let newArray: [SuperClass] = A.arrayA + B.arrayB
Here I got an error: type of expression is ambiguous without more context
How can I resolve this?
Upvotes: 1
Views: 176
Reputation: 80801
It's worth noting that because generics are invariant in Swift, without any compiler magic, not even this would compile:
let newArray : [SuperClass] = A.arrayA
as you're attempting to convert an Array<A>
to an Array<SuperClass>
, which are entirely unrelated types (see this Q&A).
Although, for some native collection types in Swift (Array
being one of them), the compiler does do some magic which allows for implicit conversions between collections with elements of a subclass type to those with elements of a superclass type (as well as concrete types to abstract types that they conform to).
However, when it comes to this line:
let newArray : [SuperClass] = A.arrayA + B.arrayB
it's simply too much for the compiler to resolve, as it both has to locate the correct +
overload for the context, which could be any of the following:
public func +<C : RangeReplaceableCollection, S : Sequence where S.Iterator.Element == C.Iterator.Element>(lhs: C, rhs: S) -> C
public func +<C : RangeReplaceableCollection, S : Sequence where S.Iterator.Element == C.Iterator.Element>(lhs: S, rhs: C) -> C
public func +<RRC1 : RangeReplaceableCollection, RRC2 : RangeReplaceableCollection where RRC1.Iterator.Element == RRC2.Iterator.Element>(lhs: RRC1, rhs: RRC2) -> RRC1
and it has to infer that you want the element type for both A.arrayA
and B.arrayB
to be converted to SuperClass
.
In order to help it, you could simply cast both sides to [SuperClass]
:
let newArray = A.arrayA as [SuperClass] + B.arrayB as [SuperClass]
or do:
let newArray : [SuperClass] = A.arrayA as [SuperClass] + B.arrayB
Or (the most interesting solution), you could define an overload for +
that deals specifically with two Array<T>
operands:
func +<T>(lhs: [T], rhs: [T]) -> [T] {
var lhs = lhs
lhs.reserveCapacity(lhs.count + rhs.count)
lhs.append(contentsOf: rhs)
return lhs
}
(this has a similar implementation to the existing standard library overloads)
Now this works:
let newArray : [SuperClass] = A.arrayA + B.arrayB
From what I can tell, the compiler only has a problem when the generic constraint for the element types of the operands to be equal is secondary (i.e as a where
clause). This overload however has it as its primary constraint.
Upvotes: 1
Reputation: 7893
Just Posting something i tried on my desk:
class User:NSObject{
var name: String
var age: Int
init(n:String, a:Int) {
self.name = n
self.age = a
}
}
class Section: User {
static let arrayA: [Section] = [Section(n: "John", a: 20), Section(n: "Mariah", a: 18)]
}
class Library: User {
static let arrayB: [Library] = [Library(n: "Martin", a: 20), Library(n: "Mariah", a: 18)]
}
Finally:
let newArray:[User] = Section.arrayA.map { $0 as User } + Library.arrayB.map { $0 as User }
print(newArray)
Output:
(lldb) po newArray
▿ 4 elements
- [0] : <TestSwift2.Section: 0x7fdb4b43d460>
- [1] : <TestSwift2.Section: 0x7fdb4b43d4e0>
- [2] : <TestSwift2.Library: 0x7fdb4b43e4b0>
- [3] : <TestSwift2.Library: 0x7fdb4b43e4e0>
(lldb) po newArray[0].age
20
(lldb) po newArray[0].name
"John"
Upvotes: 0
Reputation:
You have to convert each array from types [A]
and [B]
to type [SuperClass]
. This is best done with map
. Then you can combine them.
class SuperClass : CustomStringConvertible {
var description: String { return "super" }
}
class A: SuperClass {
let value: String
init(_ value: String) { self.value = value }
static let arrayA: [A] = [A("a"), A("b"), A("c")]
override var description: String { return "\(value)" }
}
class B: SuperClass {
let value: Int
init(_ value: Int) { self.value = value }
static let arrayB: [B] = [B(1), B(2), B(3)]
override var description: String { return "\(value)" }
}
// convert each array to [SuperClass] and combine them
let newArray: [SuperClass] = A.arrayA.map { $0 as SuperClass } + B.arrayB.map { $0 as SuperClass }
print(newArray) // -> "[a, b, c, 1, 2, 3]\n"
Upvotes: 2