请参阅下面的自我举例。编译器报告最后一行(由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 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
答案 0 :(得分:4)
显式向下转换变量:
let short = SimpleTrain<T>() as ShortType
或返回值:
return short as ShortType
解决了这个问题。
<强>更新强>
当我回答这个问题时,我想知道自己如何将Train
协议用作返回类型,因为它是类型化的。
看看这段代码:
protocol ProtocolWithNoAlias {
}
protocol ProtocolWithAlias {
typealias AliasType
}
var x: ProtocolWithNoAlias?
var y: ProtocolWithAlias?
最后一行报告为编译错误:
Protocol 'ProtocolWithAlias' can only be used as a generic constraint because it has Self os associated type requirements
这意味着您不能将ProtocolWithAlias
用作具体类型,这反过来意味着您无法声明具有ProtocolWithAlias
类型的变量,因此定义返回它的函数没有意义。
我在官方文档中找不到任何关于它的提及,但我确信我在某个地方读过,我只是不记得在哪里。
结论:我解释了您遇到麻烦的错误:
SimpleTrain<T> is not convertible to 'ShortType'
作为协议类型化的直接后果。
请注意,这是我的个人意见,因为除了包含和不包含别名的代码段测试协议之外,我现在无法证明它。
答案 1 :(得分:3)
首先,您想从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]
这样的协议方法可以提供良好的桥梁。保持对您的设计的灵活性,其中一个几乎肯定会成功。
答案 2 :(得分:0)
你有使用协议吗?我对类型理论的掌握并不是很好,但问题似乎回到了#34;更高级的类型&#34;,根据Apple线程和上面的讨论。
也就是说,如果您可以尝试使用抽象基类进行近似(但实际上它不是抽象的,但不适用于结构),请使用以下格式:
class AnyTrain<CarriageType> {
func addCarriage(carriage: CarriageType) {}
func shortTrain() -> AnyTrain<CarriageType> { return AnyTrain<CarriageType>() }
}
class SimpleTrain<T> : AnyTrain<T>, Printable {
private var carriages: [T] = [T]()
override func addCarriage(carriage: T) {
carriages.append(carriage)
}
override func shortTrain() -> AnyTrain<T> {
let short = SimpleTrain<T>()
short.addCarriage(carriages[0])
return short //COMPILE ERROR: SimpleTrain<T> is not convertible to 'ShortType'
}
var description:String {
return "Train: \(carriages)"
}
}
let train = SimpleTrain<Int>()
train.addCarriage(1)
train.addCarriage(2)
train.addCarriage(3)
let shortTrain = train.shortTrain()
println(shortTrain)
// obviously you can refer to it as the base
let anotherTrain:AnyTrain<Int> = SimpleTrain<Int>()
anotherTrain.addCarriage(3)
anotherTrain.addCarriage(2)
anotherTrain.addCarriage(1)
let anotherShortTrain = anotherTrain.shortTrain()
println(anotherShortTrain)
输出:
火车:[1]
火车:[3]
我已经尝试过这种技术用于我有抽象层次结构的情况,其中一种类型是共同的,而其他类型不同但不影响函数签名。通过使用&#34;抽象类&#34;,我可以创建一个&#34;任何类型的数组,这个类型有一个共同的通用类型(而其他类型可以不同)&#34;