在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的问题,它也使用类型推断?
答案 0 :(得分:1)
你提到的那个问题似乎在F#中遇到麻烦,只是通过声明对某些界面的一致性来推断某些属性的正确类型。 IThings
是FSharpThings
符合的接口。
让我觉得Swift中的等价物是:
class A { }
class B: A { }
protocol MyProtocol {
var value: A { get set }
}
class Test: MyProtocol {
var value = B()
}
这会导致警告Test
不符合协议。
您可以在此处看到问题:编译器应该为value
推断出什么类型?如果它执行了标准推断,则会推断value
中的Test
是B
,而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
实例时,它以相同的方式对实例进行子类化,在执行操作时引用基类。
由于子类继承了父类的所有成员,因此能够在需要超类的任何地方使用它是合乎逻辑的。
请注意,此功能与类型推断无关,它是使用子类实例的能力,其中超类与类型兼容性相关,而不是类型推断。