无法将类实例分配给其协议类型?

时间:2014-10-08 22:41:45

标签: ios generics swift interface compiler-errors

请参阅下面的自我举例。编译器报告最后一行(由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

3 个答案:

答案 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;