为什么在OOP中有一个类的选项被标记为永远不会从祖先继承?

时间:2012-09-25 17:32:03

标签: oop inheritance

C#中的“sealed”关键字,Java中的Final。

因为我几乎从不创建任何图表而且我只使用已经完成的类(来自框架)我仍然在多年之后不知道为什么有人会“锁定”一个类,所以它永远不会被扩展/继承。

有用吗? 有没有什么危害使得所有类都可以从继承中扩展掉掉“密封它”的可能性?

很抱歉在2012年问OOP是微不足道的,但我想收到一个很好的解释和/或一个很好的阅读来源!因为对我来说没用,我无法相信它只是一个简单的概念。

但无论我在哪里搜索答案都是一样的:“标记一个类,防止它从其他人那里继承。”。

2 个答案:

答案 0 :(得分:6)

  1. 安全性 - 继承类可能有权访问基类的内部部分,打破封装

  2. 保留合同 - 继承类可能会破坏基类提供的合同。见Liskov替代原则

  3. 不变性 - 特殊情况2. - 继承类可以将可变字段引入不可变类

  4. 性能 - 虚拟机可以积极优化此类,例如假设方法永远不会被覆盖,避免虚拟调用

  5. 简洁 - 实现像equals()这样的方法更简单,而不必担心继承

答案 1 :(得分:2)

原因是如果您允许子类化,并且对子类可以覆盖的方法没有限制,那么保留行为的基类的更新仍然可以破坏子类。换句话说,从一个没有专门设计用于扩展的类继承是不安全的(通过将特定方法指定为可覆盖并使所有其他方法final。)这称为脆弱基数类问题 - 请参阅this paper以获取示例,并this paper进行更全面的问题分析。

如果基类不是为继承而设计的,而且它是公共API(可能是库)的一部分,那么现在你遇到了严重的麻烦。您无法控制谁对您的代码进行子类化,并且您无法通过查看基类来了解更改对于子类是否安全最重要的是,您要么设计无限继承,要么完全禁止继承。


请注意,可能有有限个子类。这可以通过私有构造函数和内部类来实现:

class Base {
    private Base() {}

    (public|private) static final class SubA extends Base { ... }
    (public|private) static final class SubB extends Base { ... }
    (public|private) static final class SubC extends Base { ... }
}

内部类可以访问私有构造函数,但是顶级类不是,所以你只能从内部继承它。这允许您表达"类型Base的值可以是SubA或SubB OR SubC"的概念。这里没有危险,因为Base通常是空的(你真的从Base继承任何东西)并且所有子类都在你的控制之下。

您可能认为这是一种代码味道,因为您已经了解了子类的类型。但是你应该意识到这种实现抽象的替代方法是对接口的补充。当您拥有有限数量的子类时,很容易添加新函数(处理固定数量的子类),但很难添加新类型(每个现有方法都需要更新以处理新的子类)。当您使用接口或允许无限制的子类化时,添加新类型(实现固定数量的方法)很容易,但添加新方法很困难(您需要更新每个类)。一方面的优势是另一方面的弱点。有关该主题的更详细讨论,请参阅On Understanding Data Abstraction, Revisited编辑:我的错误;我链接的论文谈到了不同的二元性(ADT与对象/接口)。我实际想到的是Expression Problem


避免遗传的原因也不那么严重。假设您从A类开始,然后在子类B中实现一些新功能,稍后在子类C中添加另一个功能。然后您意识到您需要这两个功能,但是您无法创建扩展B和B的子类BC C.即使你设计了继承并防止脆弱的基类问题,也有可能被它咬住。除了上面显示的模式,most uses of inheritance are better replaced with composition - 例如,使用Strategy Pattern(或者如果您的语言支持,则只使用高阶函数。)