通用函数和子类。任何人都可以解释这个编译错误吗?

时间:2017-02-23 17:31:11

标签: swift generics compiler-errors

有人可以向我解释为什么本例中的最后一个案例是编译错误吗?

class A{}
class B: A{}

protocol Finder{
    func find< T: A>(_ f: (T)->Bool)  -> T?
}

class FinderImpl : Finder{
    func find<T : A>(_ f: (T) -> Bool)   -> T? {
        //In the real code I use T to find the instance. I.e. CoreData
        return nil
    }
}

let finder = FinderImpl()

//OK 
let optionalB : B? =  finder.find{_ in true}
let a : A =  finder.find{_ in true}!
let b : B =  finder.find{(b: B) in true}!

//Compile Error
let b2 : B =  finder.find{_ in true}!

让a。编译器使用(A) -> Bool.作为闭包类型。然后返回类型是A.

让b。这会编译,因为闭包有明确的信息:(B)->Bool

让optionalB。我想知道为什么这个案例有效,这里闭包也没有任何信息。区别在于! operator

错误:在最后一种情况下,编译器无法推断传递给func find的闭包类型。它建议我转换as! B,因为它认为闭包类型是(A)->Bool。它没有使用b2 B的参考类型。

重要提示:我无法转换as! B,因为我需要find函数来实际使用类型B.如果我转换as! B,该函数将使用类型A并获取错误的实例。代码会编译但结果会出错。

如果我删除了T:A限制,则没有编译错误。

这对我来说看起来像编译错误。我认为编译器应该使用b2的类型来知道闭包是(B)->Bool然后是结果类型的find。封闭中的T:A限制和缺少类型信息导致它失败。

我在这里遗漏了什么吗?有什么想法吗?

1 个答案:

答案 0 :(得分:0)

因为Swift无法在最后一个例子中推断出返回类型。

在您的通用函数中,您声明T必须是AA的子类:

func find<T : A>(_ f: (T) -> Bool) -> T? {
    return nil
}

当你在闭包中指定类型时,Swift可以推断出T实际上是什么。这就是为什么这样做的原因:

finder.find{(b: B) in true}!
                ^ now T == B

在这里,您没有提供任何类型信息,因此编译器所依赖的唯一内容是泛型函数的声明,即T : A

finder.find{ _ in true}! // this return A
             ^ what type is this? The compiler doesn't have any extra
               information so it must be A according to the declaration

您可以通过进行显式转换来获取编译器:

let b2 : B =  finder.find{ _ in true}! as! B

或提供类型信息:

let b3 : B =  finder.find{ (_: B) in true}!

(这是为了让它只能编译。代码显然会在运行时崩溃,因为你强制解包一个零值)