请参见下面的自包含示例。编译器在最后一行(由标记为COMPILE ERROR)报告错误,在该行中,我将实例分配给SimpleTrain它(根据我的最佳判断)符合的协议类型。如何编译?我究竟做错了什么?还是这个编译器问题?
COMPILE ERROR
SimpleTrain
protocol Train { typealias CarriageType func addCarriage(carriage: CarriageType) func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType } class SimpleTrain<T> : Train { typealias CarriageType = T private var carriages: [T] = [T]() func addCarriage(carriage: T) { carriages.append(carriage) } func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType { let short = SimpleTrain<T>() short.addCarriage(carriages[0]) return short //COMPILE ERROR: SimpleTrain<T> is not convertible to 'ShortType' } }
编辑: 即使我明确地向下转换了shortTrain上面的返回类型(以便上面的代码片段的最后一行读取return short asShortType),如安东尼奥在调用函数时仍然存在编译错误shortTrain:
shortTrain
return short asShortType
let s = SimpleTrain<String>() s.addCarriage("Carriage 1") s.addCarriage("Carriage 2") let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train' let b = s.shortTrain<SimpleTrain<String>>() //ERROR: cannot explicitly specialize a generic function
首先,您想从devforums上读到规范的线程。您特别想跳过阅读jckarter的评论。
现在转到已编辑的问题:
let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train'
这是因为您没有给编译器足够的信息来确定的类型a。想一想它所看到的:
a
func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType { let a = s.shortTrain()
编译器需要a在编译时确定类型,并且不能处理抽象类型。它需要一个完全指定的类型ShortType(确定所有内容;指定所有泛型,解析所有类型别名)。它环顾四周,并看到对的一些约束ShortType,但看不到实际提供类型的任何东西。它所具有的只是a,没有给出任何提示。
ShortType
不幸的是,这使您不得不明确地告诉它要发生的事情。
let a: SimpleTrain<String> = s.shortTrain()
这可能与您想要的相反,但现在您可以在Swift中完成所有操作。Swift团队已经表明(很多次)他们非常了解与关联类型有关的这些问题(以及类型系统中的其他一些相关弱点)。他们特别了解可以处理这些事情的Scala类型系统,并且与当前的Swift类型系统有很多共同点(尽管以我的经验,在Scala中使用复杂的与路径相关的关联类型也可以导致头发撕裂)。
就是说,从您的示例中并不能完全清楚您计划如何使用此功能。某些火车会返回与shortTrain()不同的类型吗?
我发现这些问题通常在一般情况下会爆发,但在您面前的应用程序的特定情况下往往可以解决。很难在Swift中用代码来解决任何问题来构建真正的任意类型,但是当您专注于真正需要的类型时,通常会奏效。例如,如果shortTrain()返回Self,这显然会变得更简单。如果呼叫者知道所需的结果类型,则init(shorten:)可能可以处理它。类似的协议方法shortCarriages() -> [CarriageType]可以提供良好的桥梁。保持设计的灵活性,几乎可以肯定其中之一是可行的。
shortTrain()
Self
init(shorten:)
shortCarriages() -> [CarriageType]