为什么类需要“最终”?当采用类型为“自我”的财产协议时? [英] Why do classes need to be "final" when adopting a protocol with a property with type "Self"?

查看:155
本文介绍了为什么类需要“最终”?当采用类型为“自我”的财产协议时?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以使用泛型协议,但我试图理解泛型协议的一些限制背后的推理。



示例:

 内部协议ArchEnemyable {
var archEnemy:Self? {
获得
}
}

内部最终类人形生物:ArchEnemyable {
internal var archEnemy:Humanoid? //是Self的采用,仅在类为final时有效

内部初始化(otherOne pOtherOne:Humanoid?){
self.archEnemy = pOtherOne
}
}

我没有找到任何不适用的例子,它不是最终的。你们中的一个人是否知道一个例子,为什么你需要让这个类成为最终的?



没有协议,代码工作正常:

 内部类Humanoid {
internal var archEnemy:Humanoid? //不需要协议

内部初始化(otherOne pOtherOne:Humanoid?){
self.archEnemy = pOtherOne
}
}

内部类Human:人形{
覆盖内部var archEnemy:人形? {
get {
return self
}
set {
}
}
}

内部类Ape :人形{
}

内部让人类:人形=人类(otherOne:零)
内部让ape:人形=猿(otherOne:nil)
人类。 archEnemy = ape

我首先想到的就是这样,因为子类不能履行该协议,因为自我不能是具体类型的子类。



重写属性不能改变类型。因此,人类不可能保证人类守护神,这将违反协议。



但是,方法正是如此!



所以方法不违反协议,即使它对我来说似乎一见钟情。



使用Self作为参数类型的协议中的方法声明并不会将采用的类限制为最终的。

您必须替换Self与具有使用Self的属性一样的具体类型。



在可以继承子类的子类中,子类不会将Self替换为具体类型,因此它保持为超类类型。 p>

可以添加另一个具有不同签名的方法,但它不是来自协议的方法,因为 Self 是只有指的是超类的具体类型。



方法示例:

 <$ c $内部协议MythicalCreature {
func make LoveWith(otherThing:Self) - > Void
}

内部类Kraken:MythicalCreature {
internal func makeLoveWith(otherThing:Kraken) - > Void {
print(与其他kraken做爱)
}
}

内部类FlyingKraken:Kraken {
var canFly:Bool = true

//这是可能的,但没有义务添加多态,Self不会自动被子类'具体类型
内部函数makeLoveWith(otherThing:FlyingKraken) - >替换。 Void {
print(与空中的其他克拉肯相爱)
}
}

让krakenWithDynamicFlyingKraken:Kraken = FlyingKraken()
let flyKraken:FlyingKraken = FlyingKraken()
krakenWithDynamicFlyingKraken(flyKraken)//使用Kraken的方法,没有动态调度

内部类Hydra:MythicalCreature {
内部函数makeLoveWith(otherThing: Hydra) - > Void {
print(与其他龙相爱)
}
}

内部类FlyingHydra:Hydra {
}

内部类BurrowingHydra:Hydra {
}

让hydra1:Hydra = FlyingHydra()
让hydra2:Hydra = BurrowingHydra()
hydra1.makeLoveWith hydra2)// Self不是子类的具体类型

然后,协议与方法是,自我只是采用它的类的具体类型,而不是它的子类。

所以问题仍然存在:

>


为什么苹果限制使用具有Self类型的属性的协议的类是最终的,当它们与方法不一样时?我怀疑你遇到的问题源于Swift中读写属性不变的事实。如果你考虑一个没有 Self 的更基本的例子:

  class A { } 
class B:A {}

class Foo {
var a = A()
}

class Bar:Foo {
//错误:无法用协变类型'B'覆盖类型'A'的可变属性'a'
覆盖var a:B {
get {
return B() //可以返回B(因为B符合A)
}
set {} //只能接受B(但不是A,因此非法!)
}
}

我们无法覆盖类型为 A 类型为 B 的属性。这是因为尽管阅读是协变的,但写作是相反的。换句话说,当我们重写它时,我们总是可以让getter返回给定属性中更具体的类型 - 因为该特定类型将始终符合/从属性的基类型继承。然而,我们不能使setter更具类型限制性,因为我们现在阻止给定的类型被设置为允许我们设置原始属性声明。



因此,我们达到了这种状态,我们无法再让属性更具体或更少,因为我们重写它,从而使它不变。



Self 类型的属性需求,因为 Self 指的是实现该协议的任何运行时类型,因此它的静态类型将与类声明的类型相同。因此,当你使用这个协议要求来给一个给定的类子类化时,该子类中的 Self 的静态类型是子类的类型 - 因此该属性需要是协变的为了达到这个要求,它不是。

然后你可能会想知道为什么编译器不允许你满足这个协议要求,只有计算的属性,这是协变的。我怀疑这是由于只读计算属性可以被 settable 计算属性覆盖,从而创建不变性的事实。尽管我不完全确定为什么编译器不能在重写时选择错误,而不是完全防止只读属性。



在任何情况下,您可以通过使用方法来获得相同的结果:

 协议栏{
func foo() - > ;自我

$ b $ class Foo:Bar {

需要init(){}

func foo() - > Self {
return self.dynamicType.init()
}
}

如果您需要使用 Self 要求来实现协议中的属性,那么您的确必须使类 final 为了防止子类化,并因此违反了读写属性的协议要求。

这个原因,这一切都适用于方法输入正常工作,原因是重载。当你在子类中实现一个带有 Self 输入类型的方法时,你不会覆盖超类中的那个 - 你只需要添加另一个可以调用的方法。当你来调用这个方法的时候,Sw​​ift会喜欢那些具有更多类型签名的类型(你可以在你的例子中看到)。
$ b

(注意if你尝试在你的方法中使用 override 关键字,你会得到一个编译器错误。)

同样,当您发现 Self 引用方法输入时,您甚至不必在子类中实现新方法来考虑更改在 Self 的类型中。这是由于方法输入的矛盾,我在我在这个答案中进一步解释。基本上超类方法已经满足了子类的要求,因为你可以自由地用给定的超类型替换给定方法输入的类型。


I can use generic protocols just fine, but am trying to grasp the reasoning behind some limitations of generic protocols.

Example:

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

I do not find any example where it would not work, when it is not final. Do one of you know an example, why you need to make the class final?

Without the protocol the code works fine:

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

My first thought was that it is like this, because subclasses can not fulfill the promises of the protocol, since "Self" can not be concrete type of the subclass.

Overriding properties can NOT change the type. So it would not be possible for a "Human" to guarantee a "Human" archEnemy, which would violate the protocol.

BUT, exactly that is the case for methods!

So methods do not violate the protocol, even though it seemed for me like it at first sight.

A method declaration in a protocol which uses Self as parameter type, does not limit the adopting class to be final.

You have to replace Self with the concrete type like with properties using Self.

In methods you can subclass and the subclass does not replace Self with their concrete type, it stays the superclass type.

It is possible adding another method with a different signature, but it is not the one from the protocol, because Self is only referring to the superclass' concrete type.

Example with methods:

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

Then the promise of the protocol with the method is that Self is only the concrete type of the class adopting it, but not its subclasses.

So the question stays:

Why is Apple limiting classes that adopt protocols with properties with Self as type to be final, when they are not doing the same to methods?

解决方案

I suspect the problem you've encountered stems from the fact that read-write properties are invariant in Swift. If you consider a more basic example without Self:

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

We cannot override the read-write property of type A with a property of type B. This is because although reading is covariant, writing is contravariant. In other words, we can always make the getter return a more specific type from a given property as we override it – as that specific type will always conform/inherit from the base type of the property. However we cannot make the setter more type restrictive, as we're now preventing given types from being set that our original property declaration allowed to be set.

Therefore we reach this state where we cannot make the property any more or any less type specific as we override it, thus making it invariant.

This comes into play with a Self typed property requirement, as Self refers to the runtime type of whatever is implementing the protocol, therefore its static type will be the same type as the class declaration. Therefore when you come to subclass a given class with this protocol requirement, the static type of Self in that subclass is the type of the subclass – thus the property would need to be covariant in order to meet this requirement, which it isn't.

You may then be wondering why the compiler doesn't allow you to satisfy this protocol requirement with a read-only computed property, which is covariant. I suspect this is due to the fact that a read-only computed property can be overridden by a settable computed property, thus creating invariance. Although I'm not entirely sure why the compiler can't pick that error up at the point of overriding, rather than preventing read-only properties entirely.

In any case, you can achieve the same result through using a method instead:

protocol Bar {
    func foo() -> Self
}

class Foo : Bar {

    required init() {}

    func foo() -> Self {
        return self.dynamicType.init()
    }
}

If you need to implement a property from a protocol with a Self requirement, then you will indeed have to make the class final in order to prevent subclassing and therefore violations of the protocol requirement for read-write properties.

This reason that this all works fine with method inputs is due to overloading. When you implement a method with a Self input type in a subclass, you're not overriding the one in the superclass – you're simply adding another method that can be called. When you come to call the method, Swift will favour the one with the more type specific signature (which you can see in your example).

(Note if you try and use the override keyword on your method, you'll get a compiler error.)

Also as you've discovered for when Self refers to a method input, you don't even have to implement a new method in your subclass to take account of the change in Self's type. This is due to the contravariance of method inputs, which I explain further in this answer. Basically the superclass method already satisfies the subclass requirement, as you can freely replace the type of a given method input with its supertype.

这篇关于为什么类需要“最终”?当采用类型为“自我”的财产协议时?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆