Generics 为什么课程必须是;“最后的”;采用属性为“类型”的协议时;“自我”吗;?
我可以很好地使用通用协议,但我试图理解通用协议某些限制背后的原因 例如:Generics 为什么课程必须是;“最后的”;采用属性为“类型”的协议时;“自我”吗;?,generics,properties,swift2,protocols,Generics,Properties,Swift2,Protocols,我可以很好地使用通用协议,但我试图理解通用协议某些限制背后的原因 例如: internal protocol ArchEnemyable { var archEnemy: Self? { get } } internal final class Humanoid: ArchEnemyable { internal var archEnemy: Humanoid? // is the adoption to Self, works only whe
internal protocol ArchEnemyable {
var archEnemy: Self? {
get
}
}
internal final class Humanoid: ArchEnemyable {
internal var archEnemy: Humanoid? // is the adoption to Self, works only when class is final
internal init(otherOne pOtherOne: Humanoid?) {
self.archEnemy = pOtherOne
}
}
我没有发现任何例子,当它不是最终的时候,它会不起作用。你们当中有谁知道一个例子,为什么你们要让这门课成为期末考试
如果没有协议,代码可以正常工作:
internal class Humanoid {
internal var archEnemy: Humanoid? // Do not need to be final, without protocol
internal init(otherOne pOtherOne: Humanoid?) {
self.archEnemy = pOtherOne
}
}
internal class Human: Humanoid {
override internal var archEnemy: Humanoid? {
get {
return self
}
set {
}
}
}
internal class Ape: Humanoid {
}
internal let human: Humanoid = Human(otherOne: nil)
internal let ape: Humanoid = Ape(otherOne: nil)
human.archEnemy = ape
我的第一个想法是,它是这样的,因为子类不能实现协议的承诺,因为“Self”不能是子类的具体类型
重写属性无法更改类型。因此,“人类”不可能保证“人类”的宿敌,这将违反协议
但是,对于方法来说确实如此
因此,这些方法不会违反协议,即使对我来说,乍一看似乎是这样
协议中使用Self作为参数类型的方法声明不限制采用的类为final。
您必须使用具体类型替换Self,就像使用Self的属性一样
在方法中,您可以使用子类,而子类不会用它们的具体类型替换Self,它仍然是超类类型
可以添加另一个具有不同签名的方法,但它不是来自协议的方法,因为Self
仅引用超类的具体类型
方法示例:
internal protocol MythicalCreature {
func makeLoveWith(otherThing: Self) -> Void
}
internal class Kraken: MythicalCreature {
internal func makeLoveWith(otherThing: Kraken) -> Void {
print("Making love with other kraken")
}
}
internal class FlyingKraken: Kraken {
var canFly: Bool = true
// It is possible, but no obligation to add polymorphism, Self will not automatically be replaced by the subclass' concrete type
internal func makeLoveWith(otherThing: FlyingKraken) -> Void {
print("Making love with other kraken in the air")
}
}
let krakenWithDynamicFlyingKraken: Kraken = FlyingKraken()
let flyKraken: FlyingKraken = FlyingKraken()
krakenWithDynamicFlyingKraken(flyKraken) // USES the method of Kraken, no dynamic dispatch
internal class Hydra: MythicalCreature {
internal func makeLoveWith(otherThing: Hydra) -> Void {
print("Making love with other dragon")
}
}
internal class FlyingHydra: Hydra {
}
internal class BurrowingHydra: Hydra {
}
let hydra1: Hydra = FlyingHydra()
let hydra2: Hydra = BurrowingHydra()
hydra1.makeLoveWith(hydra2) // Self is not the concrete type of the subclasses
那么该方法协议的承诺是Self只是采用它的类的具体类型,而不是它的子类。
因此,问题依然存在:
为什么Apple会限制采用协议的类,这些协议的属性以Self为类型,而它们对方法却没有这样做?我怀疑您遇到的问题源于这样一个事实,即读写属性在Swift中是不变的。如果你考虑一个更基本的例子,没有<代码>自我<代码>:
class A {}
class B : A {}
class Foo {
var a = A()
}
class Bar : Foo {
// error: Cannot override mutable property 'a' of type 'A' with covariant type 'B'
override var a : B {
get {
return B() // can return B (as B conforms to A)
}
set {} // can only accept B (but not A, therefore illegal!)
}
}
我们不能用B
类型的属性重写A
类型的读写属性。这是因为尽管阅读是协变的,但写作却是相反的。换句话说,我们总是可以让getter在重写某个给定属性时从该属性返回一个更特定的类型,因为该特定类型将始终符合/继承该属性的基类型。但是,我们不能使setter具有更大的类型限制性,因为我们现在阻止将给定的类型设置为原始属性声明允许设置的类型
因此,我们达到这样的状态,即在重写属性时,无法使属性更多或更少地特定于类型,从而使其保持不变
这在Self
类型化属性需求中起作用,因为Self
引用实现协议的任何对象的运行时类型,因此其静态类型将与类声明的类型相同。因此,当您使用此协议要求对给定类进行子类化时,该子类中的Self
的静态类型就是子类的类型–因此属性需要是协变的,才能满足此要求,而它不是
然后,您可能想知道为什么编译器不允许您使用只读计算属性(协变)来满足此协议要求。我怀疑这是因为只读计算属性可以被可设置的计算属性覆盖,从而创建不变性。虽然我不能完全确定为什么编译器不能在重写时发现错误,而不是完全阻止只读属性
在任何情况下,您都可以通过使用方法来实现相同的结果:
protocol Bar {
func foo() -> Self
}
class Foo : Bar {
required init() {}
func foo() -> Self {
return self.dynamicType.init()
}
}
如果您需要从具有Self
要求的协议中实现属性,那么您确实必须使类final
,以防止子类化,从而违反协议对读写属性的要求
这一切在方法输入中都能正常工作的原因是重载。当您在子类中实现具有Self
输入类型的方法时,您并没有覆盖超类中的方法–您只是添加了另一个可以调用的方法。当您调用该方法时,Swift将倾向于使用更特定于类型的签名(您可以在示例中看到)
(请注意,如果尝试在方法上使用override
关键字,则会出现编译器错误。)
另外,正如您所发现的,当
Self
引用方法输入时,您甚至不必在子类中实现新方法来考虑Self
类型的变化。这是由于方法输入的反向方差,这是一个非常复杂的问题。基本上,超类方法已经满足了子类的要求,因为您可以自由地将给定方法输入的类型替换为它的超类型。使用方法将自身类型更改为子类的类型这是不正确的,这就是重点。使用Self
作为参数类型的方法也会违反协议,因为在不重载方法的情况下,子类不会使用子类的具体类型作为参数类型,而是使用超类的类型。那么他们对Self
使用了两种不同的定义吗?这会让人困惑。添加了hydra示例来说明协议中使用Self
作为参数类型的方法不会像您所说的属性那样使用Self
。这就是假设在协议中使用Self
作为最终类型来创建一个具有属性的类。@iGodric在您的示例中发生的情况是Self
是BurrowingHydra
声明中的BurrowingHydra
,因此协议期望方法makeLoveWith(其他海怪:穴居水母)
至b