Swift:将类型为T的数组传递给采用T协议数组的方法

时间:2016-03-16 03:55:01

标签: swift generics inheritance

我正在尝试编写一个具有符合MyProto的对象数组的类,并且我有一种方法可以在添加到该数组之前接受[MyProto]进行各种处理。这是一个游乐场。

protocol MyProto {
    func sayHello()
}

extension MyProto {
    func sayHello() {
        print("hello")
    }
}

struct MyStruct: MyProto {
}

class MyClass {
    var protos: [MyProto] = []
    func doSomethingAndThenStore(newProtos: [MyProto]) {
        for proto in newProtos {
            proto.sayHello()
        }
        protos.appendContentsOf(newProtos)
    }
}

let myStructs = [MyStruct(), MyStruct()]
let myClass = MyClass()
myClass.doSomethingAndThenStore(myStructs)

在最后一行,我收到错误error: cannot convert value of type '[MyStruct]' to expected argument type '[MyProto]'。如果我将其更改为myStructs as [MyProto],则错误会更改为error: cannot convert value of type '[MyStruct]' to type '[MyProto]' in coercion

如何将我的具体类型数组传递给接受一组协议的方法?

1 个答案:

答案 0 :(得分:3)

这个问题源于Swift不支持协变泛型的事实。也就是说,Array<Subclass>不是Array<Superclass>。在这种情况下,即使MyStructMyProtoArray<MyStruct>也不是Array<MyProto>

Swift不支持这一点的原因有点复杂,但归结为对于某些操作,例如数组检索,将Array<MyStruct>视为Array<MyProto>是有效的,但对于其他的,例如数组插入,这种关联实际上是相反的。您将无法在MyProto中插入Array<MyStruct>,因此Array<MyStruct>不能被视为Array<MyProto>。其他语言有解决此问题的机制,但Swift目前不支持它们。

您无法直接传递数组,但此限制有几种解决方法。最简单的说,您可以在数组上映射标识函数,以便类型检查器推断出新类型。这将隐式地将每个元素从MyStruct单独转发到MyProto

myClass.doSomethingAndThenStore(myStructs.map { $0 })

您也可以将MyClass设为通用并添加类型约束:

class MyClass<T: MyProto> {
    var protos: [T] = []
    func doSomethingAndThenStore(newProtos: [T]) {
        for proto in newProtos {
            proto.sayHello()
        }
        protos.appendContentsOf(newProtos)
    }
}