【问题标题】:Type coercion issue in SwiftSwift 中的类型强制问题
【发布时间】:2016-08-03 06:17:27
【问题描述】:

编辑:这在 Swift 3 中工作得非常好,我们现在都应该使用它 :)


如果我有两个协议XY,其中Y 实现X,为什么我不能将Y 的数组分配给[X] 类型的变量?

更奇怪的是,我可以将它一个一个转换成一个 X 数组,并且编译得很好。

protocol X { }

protocol Y: X { }

// Make a test array of Y
extension String: Y { }
let a: [Y] = [ "Hello" ]

// Make it into an array of X - this works absolutely fine
let b: [X] = [ a[0] as X ]

// Why won't this cast work?
let c: [X] = a as [X]

我认为 Y 做了 X 可以做的所有事情,这个赋值应该没问题(是的,我丢失了一些类型信息,但它至少应该编译!)

有什么想法吗?

编辑:current answer 指出,如果您使用可变数组,这是一件危险的事情——我没有得到但现在这样做了 :) 但是,如果我的数组都是不可变的,为什么 Swift 不让这会发生吗?即使c 是一个可变数组(即var c = a as [X]),为什么Swift 不会复制它,而让a 保持原样?

【问题讨论】:

  • Swift 泛型是不变的。不确定您是否可以在 swift 中使用协变泛型。
  • 这段代码的问题在于,您可以将Z 添加到同样源自X 的层次结构中。现在您的数组同时具有YZ,即使Z不是Y 的子类型。
  • @LukaJacobowitz:我认为这不是问题所在。事实上,您仍然可以单独附加符合Y 的不同类型。例如。 struct Z: X {};b.append(Z())。现在b 包含String 类型的元素和Z 类型的元素。编译器对此很好。
  • 我知道,但考虑一下:` (a as [X]).append(Z())` 现在您将 Z 添加到 Y 数组中
  • 顺便说一句,这在 Swift 3 中运行良好

标签: swift generics casting


【解决方案1】:

这不起作用,因为它可能会产生一些问题。例如:

var squares: Array<Square> = Array<Square>()
(squares as [Shape]).append(Circle())

现在我们的正方形数组中有一个圆。我们不希望这样,所以编译器不允许我们这样做。在 Scala 等其他语言中,您可以将泛型指定为 covariantcontravariantinvariant

如果 Swift 允许您使用 协变 泛型,Array&lt;Square&gt; 将是 Array&lt;Shape&gt; 的子类型。

如果使用逆变Array&lt;Square&gt; 将是Array&lt;Shape&gt; 的超类型(!)。

但是当使用 invariant 泛型时,两者都不是两者的子类型。

在您的情况下,最简单的方法可能是:

let c = a.map{$0 as X}

现在c 的类型是[X]

有关类型差异以及为什么会出现问题的更多信息,请访问this wiki page

编辑:经过进一步反复,似乎真正的问题是,协议允许默认实现,因此可能会导致问题。使用类时,您的代码将完美编译。以下是一些可能导致协议问题的代码:

protocol Shape {
    func surfaceArea() -> Double
}

extension Shape {
    func surfaceArea() -> Double {
        return 0.0
    }
}

protocol Circle : Shape {
    func getRadius() -> Double
}

extension Circle {
    func surfaceArea() -> Double {
        return getRadius() * getRadius() * 3.14
    }
}

现在,当我们在这两个协议之间向上转换时,surfaceArea() 方法返回不同的值,而 Swift 不允许这样做。

用类而不是协议尝试同样的事情,Swift 编译你当前的代码不会有任何问题。对我来说,这是 Swift 团队做出的一个奇怪的决定,但我仍然认为最好的缓解方法就是使用

let c = a.map{$0 as X}

【讨论】:

  • 您的第一个代码块由于其他原因无法正常工作。编译器错误为error: cannot use mutating member on immutable value of type '[Shape]'。这与原始问题相关。与var list = [1,2,3];(list as [Int]).append(4) 相同,我们使用完全相同的类型。请在问题下方阅读我的 cmets。
  • 很好的解释。就我而言,我知道我对这个演员表很安全,但我明白为什么不允许这样做,嘿,我会找到一种不同的方法来做我想做的事情:)
  • @appzYourLife 我的问题与可变性完全无关,而与泛型有关。那么如果代码示例无法编译怎么办,重要的是概念
  • 没错,但这里提供的解释不是您问题的答案。这不是您最后一行代码无法编译的原因。
  • @LukaJacobowitz:完全正确! 如果a是一个不可变数组,为什么我不能通过丢失类型信息来创建一个新数组c
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多