在Swift中输入强制问题

时间:2016-04-12 14:41:21

标签: swift generics casting

编辑:这在Swift 3中运行得非常好,我们现在都应该使用它们了。)

如果我有两个协议XY Y实现X,为什么我不能将Y数组分配给[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] 的变量?

更奇怪的是,我可以将它逐个转换为X数组,编译得很好。

c

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

有什么想法吗?

编辑:current answer指出,如果你使用可变数组,这是一件危险的事情 - 我现在还没有做到但是现在做了:)但是,如果我的数组都是不可改变为什么不会让Swift发生这种情况?即使var c = a as [X]是一个可变数组(即a),为什么不会将Swift复制它,让UITableViewCell extension完好无损?

1 个答案:

答案 0 :(得分:7)

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

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

现在我们在Square of Squares中有一个圆圈。我们不想要那样,所以编译器不允许我们这样做。在Scala等其他语言中,您可以指定泛型为covariantcontravariantinvariant

如果Swift允许您使用协变泛型,则Array<Square>将是Array<Shape>的子类型。

如果使用逆转Array<Square>将是Array<Shape>的超类型(!)。

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

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

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
    }
}

现在,当我们在这两个协议之间进行upcast时,surfaceArea()方法返回不同的值,而Swift不允许它。

使用类而不是协议尝试相同的事情,Swift在编译当前代码时没有遇到任何问题。对我来说,这是Swift团队的一个奇怪的决定,但我仍然认为减轻的最佳方法是使用

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