繁体   English   中英

在 Swift 中,如何防止函数在子类上被调用?

[英]In Swift, how do I prevent a function from being called on a subclass?

我有一个基类,它在某些数据结构中存储了许多其他数据对象,并且此类通过一组使我的数据结构保持同步的add / remove函数来管理我集合中的这些数据对象。

现在我去子类化这个基类,子类就像基类一样,除了它有关于它将接受什么样的数据对象的特殊规则(它查看数据对象上的值,如果值不正确则拒绝它们不对)。 由于这种“有效性”检查,我为名为change的子类创建了一个新函数。 change功能中,它检查所有传入的数据对象并验证它们是否正常,并用这些数据对象替换数据结构中的所有数据对象。

问题是我不希望有人能够创建子类对象并允许他们调用基类的add / remove函数,因为我只希望子类能够通过子类的change函数进行更改。

我还没有找到一个好的方法来“禁止”在子类中使用那些基类函数。 我可以override函数并实现空的实现,但是没有向调用者反馈该函数没有做任何事情。 如果我使用类似fatalError的东西,那不是编译时间,而是运行时间。

我的其他想法是将基类的功能分解为多个协议,并将基类更改为仅具有所有数据结构,但不符合任何协议,然后有多个子类,其中一个想要add功能可以从基础继承并另外符合add协议,但是不需要addremove的可以从基础继承,并且根本不符合任何协议,而是创建自己的change函数来修改数据结构.

这是一个更简单的层次结构来解释:

class Parent {
  
  func doThis() { print("Parent Did This") }
  func doThat() { print("Parent Did That") }
  
}

class Child: Parent {
  
  override func doThis() {
    print("Child Did This")
  }
  
  // I want this to be a compile time error
  override func doThat() {
    print("Not Supported")
    return
  }
  
}

有没有更简单的方法来“隐藏”子类中的函数?

编辑 1

为了更好地解释我提出的解决方案,以及是否有更简单的方法来使用我当前的层次结构来实现它,下面是使用某些协议的层次结构必须看起来的样子:

protocol CanDoThis {
  func doThis()
}

protocol CanDoThat {
  func doThat()
}

class Parent {
  // Important properties that all children should have
  var property1: Int = 0
  var property2: String = ""
}

class Child1: Parent, CanDoThis {
  func doThis() { print("Child1 Can Do This") }
}

class Child2: Parent, CanDoThat {
  func doThat() { print("Child2 Can Do That") }
}

class Child3: Parent, CanDoThis, CanDoThat {
  func doThis() { print("Child3 Can Do This") }
  func doThat() { print("Child3 Can Do That") }
}

免责声明

协议版本可能是更好的设计 - 请参阅Liskov Substitution Principle以及您提到的其他内容。

回答问题

您可以使用属性@available() (参见Swift -> Language Reference -> Attributes )。

class Parent {
    func doThis() { print("Parent Did This") }
    func doThat() { print("Parent Did That") }
}

class Child: Parent {
    override func doThis() {
        print("Child Did This")
    }

    @available(*, unavailable, message:"Child can't doThat")
    override func doThat() {
        print("Not Supported")
    }
}


let parent = Parent()
parent.doThis()
parent.doThat()

let child = Child()
child.doThis()
child.doThat()

您在child.doThat()上收到以下错误消息:

'doThat()' 不可用:孩子不能这样做

但是,您可以通过执行以下操作来解决这个问题(再次参见 Liskov 替换原则):

let maybeChild: Parent = Child()
maybeChild.doThis()
maybeChild.doThat()

我会使用@availability来处理这个问题。 就像是:

@available(*, deprecated=1.0, message="Do not call this method")
final func foo(){ //function you want to protect
}

每当您调用该函数时,这都会给您一个编译器警告。

有趣的是,将函数标记为unavailable会引发编译器错误,如果有人调用此函数,则无法构建应用程序。 不幸的是,没有办法在 Swift 中抑制该错误,因此对于您的良好用例也会破坏它。 也许你可以通过使用self.performSelector("foo")来解决这个问题。

另请注意,我将foo()标记为final ,以防止它被子类覆盖。

你想要的是访问控制。 Swift 对此的修饰符是internal 您可以在访问控制下的文档中阅读更多相关信息。

我认为最明显的解决方案是:

只需将基类中的添加和删除函数设置为私有即可

例子

class Parent {
  
  func doThis() { print("Parent Did This") }
  private func doThat() { print("Parent Did That") }
  
}

class Child: Parent {
  
  override func doThis() {
    print("Child Did This")
  }
  
  // This is a compile time error as the subclass will not be allowed to override this class, because it's private.
  override func doThat() {
    print("Not Supported")
    return
  }
  
}

在此示例中,覆盖 doThat 方法将导致编译时错误,因为不允许子类覆盖此方法,因为它是私有的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM