在Java中禁止继承的好理由?

cre*_*zel 163 java inheritance final

有什么理由禁止Java中的继承,例如使用单个私有无参数构造函数使用final类或类?使方法最终成功的理由是什么?

DJC*_*rth 172

这里你最好的参考是Joshua Bloch的优秀书籍"Effective Java"的第19项,名为"继承的设计和文件,或者禁止它".(这是第二版的第17项和第一版的第15项.)你应该读它,但我会总结一下.

如果祖先不是为了继承而设计的,那么继承类与父母的交互可能会令人惊讶且难以预料.

因此,课程应分为两类:

  1. 设计用于扩展的类,并提供足够的文档来描述应如何完成

  2. 课程标记为最终

如果您正在编写纯粹的内部代码,这可能有点矫枉过正.但是,在类文件中添加五个字符所需的额外工作量非常小.如果你只是为了内部消费而写作,那么未来的编码器总是可以删除'final' - 你可以把它想象成一个警告,说"这个类的设计并没有考虑到继承".

  • 请注意,使类最终可能会使测试更难(更难以存根或模拟) (32认同)
  • 好答案.(我无法抗拒地指出它是六个字符,因为还有一个空间... ;-) (9认同)
  • 如果最后一个类正在写入接口,那么模拟应该不是问题. (8认同)
  • 要了解一些Effective Java,您需要了解Josh对设计的看法.他说[类似]你应该总是设计类接口,就好像它们是公共API一样.我认为多数意见认为这种方法通常过于沉重且不灵活.(YAGNI等) (7认同)
  • 根据我的经验,如果除了我以外的任何人都将使用该代码,这并不是一种矫枉过正.我从来没有理解为什么Java默认情况下可以覆盖方法. (6认同)
  • 在没有源代码的接口上删除final是非常困难的,并且能够子类覆盖方法可能是必不可少的. (3认同)
  • 这不是库/框架编写者和应用程序编写者之间的区别.当你继承其他人编写的代码并且不能弃用设计中的任何错误时,Tom Hawtin所描述的"太沉重和不灵活"变得至关重要,因为原始开发人员认为一切都应该是公开的.通常,您应该始终设计在公共接口上实现方法的包级最终类.如果以后需要更改界面,请使用ADAPTER.如果不这样做,就意味着一旦部署软件,设计错误永远无法解决.即)Java (3认同)
  • 我在默认情况下将所有内容标记为最终版本时遇到的困难是,它假设开发人员没有花精力使类适合扩展,因此会花时间确保它实现设计良好的接口以促进测试。要么开发是懒惰的,在这种情况下没有接口并且它被标记为最终的,要么开发是面向细节的,在这种情况下类支持扩展并且不需要是“最终”。 (2认同)

Bil*_*ard 22

您可能希望将方法设置为final,以便覆盖类不能更改在其他方法中计入的行为.在构造函数中调用的方法通常被声明为final,因此在创建对象时不会产生任何令人不快的意外.


Joh*_*ley 18

如果你想强迫组合而不是继承,那么让一个类最终成功的一个原因就是如此.这通常是希望避免类之间的紧密耦合.


use*_*551 13

有3个用例可供您使用最终方法.

  1. 避免派生类重写特定的基类功能.
  2. 这是出于安全目的,其中基类提供了框架的一些重要核心功能,其中派生类不应该更改它.
  3. 最终方法比实例方法更快,因为最终方法和私有方法没有使用虚拟表概念.因此,只要有可能,尝试使用最终方法.

最终成绩的目的:

因此,没有任何机构可以扩展这些类并改变它们的行为.

例如:Wrapper类Integer是一个最终类.如果该类不是final,那么任何人都可以将Integer扩展到他自己的类中并更改整数类的基本行为.为了避免这种情况,java将所有包装类作为最终类.

  • 即使您将所有变量和方法都作为最终变量,其他人也可以继承您的类并添加额外的功能并更改您的类的基本行为. (3认同)
  • >如果该类不是final,那么任何人都可以将Integer扩展到他自己的类中并更改整数类的基本行为.让我们说这是允许的,这个新类叫做```DerivedInteger```,它仍然没有改变原来的Integer类,而且使用```DerivedInteger```的人自己承担风险,所以我仍然不明白为什么这是一个问题. (2认同)

Ray*_*yek 12

你可能想制作不可变对象(http://en.wikipedia.org/wiki/Immutable_object),你可能想创建一个单例(http://en.wikipedia.org/wiki/Singleton_pattern),或者你可能想要防止有人因效率,安全或安全原因而超越方法.


Jon*_*eet 6

传承就像一个电锯 - 非常强大,但在错误的手中可怕.您要么设计一个要继承的类(这可能会限制灵活性并且需要更长的时间),或者您应该禁止它.

请参阅Effective Java第2版第16和17项,或我的博客文章"继承税".


Mec*_*cki 5

嗯...我能想到两件事:

您可能有一门处理某些安全问题的课程。通过对其进行子类化并向您的系统提供其子类化版本,攻击者可以规避安全限制。例如,您的应用程序可能支持插件,并且如果插件可以对您的安全相关类进行子类化,则它可以使用此技巧以某种方式将其子类化版本偷运到位。然而,这实际上是 Sun 必须处理的有关 applet 等的问题,也许不是一个现实的情况。

更现实的方法是避免对象变得可变。例如,由于字符串是不可变的,因此您的代码可以安全地保留对它的引用

 String blah = someOtherString;
Run Code Online (Sandbox Code Playgroud)

而不是先复制字符串。但是,如果您可以子类化 String,则可以向其添加允许修改字符串值的方法,现在没有代码可以再依赖于如果只是像上面那样复制字符串,则字符串将保持不变,而必须复制细绳。