Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift array of type [SuperClass] with elements of type [Subclass]

Tags:

arrays

swift

Can someone explain why this code throws error?

class Base {}
class SubclassOfBase: Base {}

let baseItems = [Base](count: 1, repeatedValue: Base())
let subclassItems = [SubclassOfBase](count: 3, repeatedValue: SubclassOfBase())

var items = [Base]()
items.append(SubclassOfBase()) //OK
items.appendContentsOf(baseItems) //OK
items.appendContentsOf(subclassItems) //cannot invoke with argument of type [SubclassOfBase]
items.append(subclassItems.first!) //OK

And next question: Is the only way to add subclass elements is by adding them one by one in for loop?

like image 371
Kubba Avatar asked Oct 21 '15 09:10

Kubba


1 Answers

If you check the headers:

public mutating func append(newElement: Element)

public mutating func appendContentsOf<C : CollectionType where C.Generator.Element == Element>(newElements: C)

Note the difference in type specifiers. While append enables you to add anything that is an Element, that is, including sublasses, appendContentsOf forces you to use an array with exactly the same element type (subclasses not allowed).

It would work with:

let subclassItems = [Base](count: 3, repeatedValue: SubclassOfBase())

I would consider this to be a bug because this could be easily fixed by improving the function header (well, this also needs an extension to where clauses because detecting generic subtypes is impossible right now).

Some possible workarounds:

  1. Append directly for every item

    subclassItems.forEach {items.append($0)}
  2. Declare a helper method for Arrays (will work for Array, not for generic CollectionType)

    extension Array {
        public mutating func appendContentsOf(newElements: [Element]) {
            newElements.forEach {
                self.append($0)
            }
        }
    }
  3. Direct cast

    items.appendContentsOf(subclassItems as [Base])
like image 113
Sulthan Avatar answered Oct 10 '22 18:10

Sulthan