请参见下面的自包含示例。编译器在最后一行(由COMPILE ERROR
标记)上报告错误,在该行中,我将SimpleTrain
的实例分配给它(根据我的最佳判断)符合的协议(protocol)类型。如何进行编译?我究竟做错了什么?还是这个编译器问题?
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 as ShortType
)作为suggested by Antonio时,调用函数shortTrain
时仍然存在编译错误: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上阅读canonical thread。您特别想跳过阅读jckarter的评论。
现在转到已编辑的问题:
let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train'
这是因为您没有给编译器足够的信息来确定
a
的类型。想一想它所看到的:func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType {
let a = s.shortTrain()
编译器需要在编译时弄清
a
的类型,并且不能处理抽象类型。它需要为ShortType
指定一个完全指定的类型(所有内容均已明确;指定了所有泛型,并解析了所有类型别名)。它环顾四周,并且看到对ShortType
的一些约束,但没有看到任何实际提供类型的东西。它所拥有的只是a
,它没有给出任何提示。不幸的是,这使您不得不明确地告诉它您想要发生的事情。
let a: SimpleTrain<String> = s.shortTrain()
这可能与您想要的相反,但是这就是您现在可以在Swift中执行的所有操作。 Swift团队已经表明(很多次)他们非常了解与关联类型有关的这些问题(以及类型系统中的其他一些相关弱点)。他们特别了解可以处理这些事情的Scala类型系统,并且与当前的Swift类型系统有很多共同点(尽管以我的经验,在Scala中使用复杂的与路径相关的关联类型也可以导致头发撕裂)。
就是说,从您的示例中并不能完全清楚您计划如何使用此功能。某些火车会返回与shortTrain()不同的类型吗?
我发现这些问题通常在一般情况下会爆发,但在您面前的应用程序的特定情况下往往可以解决。很难在Swift中用代码来解决任何问题来构建真正的任意类型,但是当您专注于真正需要的类型时,这种方法通常会奏效。例如,如果
shortTrain()
返回了Self
,这显然会变得更简单。如果调用者知道所需的结果类型,则init(shorten:)
可能可以处理它。像shortCarriages() -> [CarriageType]
这样的协议(protocol)方法可以提供良好的桥梁。保持设计的灵活性,几乎可以肯定其中之一会奏效。关于ios - 无法将类实例分配给其协议(protocol)类型?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26267597/