Swift Generics:无法将类型的值转换为预期的参数类型

fra*_*yan 9 generics swift

这是我的代码:

protocol SomeProtocol {
}

class A: SomeProtocol {
}

func f1<T: SomeProtocol>(ofType: T.Type, listener: (T?) -> Void) {
}

func f2<T: SomeProtocol>(ofType: T.Type, listener: ([T]?) -> Void) {
}

func g() {
    let l1: (SomeProtocol?) -> Void = ...
    let l2: ([SomeProtocol]?) -> Void = ...
    f1(ofType: A.self, listener: l1) // NO ERROR
    f2(ofType: A.self, listener: l2) // COMPILE ERROR: Cannot convert value of type '([SomeProtocol]?) -> Void' to expected argument type '([_]?) -> Void'
}
Run Code Online (Sandbox Code Playgroud)

第二个闭包有一个泛型类型对象数组的参数有什么问题?

Ham*_*ish 8

Swift 4.1更新

这是一个在此pull请求中修复的错误,它将使其成为Swift 4.1的发行版.您的代码现在可以在4.1快照中按预期编译.


Pre Swift 4.1

这看起来就像你只是将编译器拉得太远了.

  • 它可以处理从子类型元素的数组超类型元素,例如数组转换[A][SomeProtocol]-这是协方差.值得注意的是,数组在这里始终是一个边缘情况,因为任意泛型是不变的.某些集合,例如Array,只是从编译器获得特殊处理,允许协方差.

  • 它可以处理超类型的函数的参数与子类型的参数,例如功能转换(SomeProtocol) -> Void(A) -> Void-这是逆变.

然而,似乎它目前不能一气呵成(但实际上它应该能够;随意提交错误).

对于它的价值,这与泛型无关,以下再现了相同的行为:

protocol SomeProtocol {}
class A : SomeProtocol {}

func f1(listener: (A) -> Void) {}
func f2(listener: ([A]) -> Void) {}
func f3(listener: () -> [SomeProtocol]) {}

func g() {

    let l1: (SomeProtocol) -> Void = { _ in }        
    f1(listener: l1) // NO ERROR

    let l2: ([SomeProtocol]) -> Void = { _ in }
    f2(listener: l2) 
    // COMPILER ERROR: Cannot convert value of type '([SomeProtocol]) -> Void' to
    // expected argument type '([A]) -> Void'

    // it's the same story for function return types
    let l3: () -> [A] = { [] }
    f3(listener: l3)
    // COMPILER ERROR: Cannot convert value of type '() -> [A]' to
    // expected argument type '() -> [SomeProtocol]'
}
Run Code Online (Sandbox Code Playgroud)

在修复之前,在这种情况下,一种解决方案是简单地使用闭包表达式作为两种函数类型之间的蹦床:

// converting a ([SomeProtocol]) -> Void to a ([A]) -> Void.
// compiler infers closure expression to be of type ([A]) -> Void, and in the
// implementation, $0 gets implicitly converted from [A] to [SomeProtocol].
f2(listener: { l2($0) })

// converting a () -> [A] to a () -> [SomeProtocol].
// compiler infers closure expression to be of type () -> [SomeProtocol], and in the
// implementation, the result of l3 gets implicitly converted from [A] to [SomeProtocol]
f3(listener: { l3() })
Run Code Online (Sandbox Code Playgroud)

并且,适用于您的代码:

f2(ofType: A.self, listener: { l2($0) })
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为编译器将闭包表达式推断为类型([T]?) -> Void,可以传递给它f2.在封闭件的实施方式中,编译器随后执行的一个隐式转换$0[T]?[SomeProtocol]?.

而且,正如多米尼克暗示的那样,这个蹦床也可以作为额外的重载f2:

func f2<T : SomeProtocol>(ofType type: T.Type, listener: ([SomeProtocol]?) -> Void) {
    // pass a closure expression of type ([T]?) -> Void to the original f2, we then
    // deal with the conversion from [T]? to [SomeProtocol]? in the closure.
    // (and by "we", I mean the compiler, implicitly)
    f2(ofType: type, listener: { (arr: [T]?) in listener(arr) })
}
Run Code Online (Sandbox Code Playgroud)

允许你再次将其称为f2(ofType: A.self, listener: l2).