为什么Swift中的自动向上转换很好

时间:2018-03-16 20:12:55

标签: swift types casting type-conversion

在Swift中,我们可以按如下方式创建自定义UICollectionViewCell:

class MyCell : UICollectionViewCell {}

接下来,如果我们有方法:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {}

我们希望我们返回UICollectionViewCell,我们可以毫无问题地返回MyCell的实例。

在F#中,我们必须首先进行以​​下演员:

thisCell :> UICollectionViewCell否则F#会抱怨我们正在返回错误类型的对象。我理解这是F#中与类型推断相关的问题,如下所述:Why isn't up-casting automatic in f#

为什么这不是Swift的问题,它也使用类型推断?

3 个答案:

答案 0 :(得分:1)

你提到的那个问题似乎在F#中遇到麻烦,只是通过声明对某些界面的一致性来推断某些属性的正确类型。 IThingsFSharpThings符合的接口。

让我觉得Swift中的等价物是:

class A { }

class B: A { }

protocol MyProtocol {
    var value: A { get set }
}

class Test: MyProtocol {
    var value = B()
}

这会导致警告Test不符合协议。

您可以在此处看到问题:编译器应该为value推断出什么类型?如果它执行了标准推断,则会推断value中的TestB,而Test可以随意调用B方法。但是,如果其他东西试图将value更改为某个A实例,比如协议说它可以:那么Test B方法调用value上的方法会怎样?失败。

另一个可能的替代方案是编译器推断value的类型为A。这对于编译器来说会很复杂,更糟糕的是,程序员编写Test代码会让人感到困惑。任何看过代码的程序员都会感到困惑,为什么他们不能调用B方法,因为他们逻辑上假设它是基于声明的类型B。这将是一团糟。

因此,编译器将要求您将其显式声明为类型A。一种解决方案是手动上传以消除任何歧义:

class Test: MyProtocol {
    var value = B() as A
}

或者,在Swift中更自然,可以明确地声明类型符合协议:

class Test: MyProtocol {
    var value: A = B()
}

cellForItemAt情况不同。您没有尝试让编译器推断某些属性的类型。它准确地知道cellForItemAt返回什么,并且所有编译器关心的是此方法返回的是UICollectionViewCell。它非常正确,并不关心它是UICollectionViewCell还是它的子类。

最重要的是,cellForItemAt场景与其他问题中提供的场景非常不同。

答案 1 :(得分:0)

这是Swift的面向对象设计。我不知道F#中的工作原理如何,但这不是问题所在。

Swift的继承模型非常简单,与现实生活相似:类B的任何子类A也是类A的实例。

与现实生活相同:你基本上是Mammal。考虑这个(非常奇怪)示例的基类。但您也是Human类型的实例。成为Human并不意味着你不再是Mammal

Mammal
|--- Human
     |--- Man
     |--- Woman

现在把这个概念用Swift进行编程:

UICollectionViewCell
|--- MyCell
|--- AnotherCell

考虑到上述模型,MyCell(...) is UICollectionViewCell等于true并且MyCell实例被接受作为返回值现​​在完全正常了。只是不要混淆作为类实例的对象和这个类是“最精确/下游”类型。

答案 2 :(得分:0)

原因很简单:Swift支持面向对象编程,因此它遵循OOP原则之一 - 多态。这正是UICollectionView所做的:在处理所有UICollectionViewCell实例时,它以相同的方式对实例进行子类化,在执行操作时引用基类。

由于子类继承了父类的所有成员,因此能够在需要超类的任何地方使用它是合乎逻辑的。

请注意,此功能与类型推断无关,它是使用子类实例的能力,其中超类与类型兼容性相关,而不是类型推断。