Javaで継承を禁止する理由は何ですか?


178

Javaでの継承を禁止する正当な理由は何ですか(たとえば、最終的なクラスを使用したり、単一のプライベートなパラメーターのないコンストラクターを使用したクラスを使用したりすることによって)。メソッドをfinalにする正当な理由は何ですか?


1
ペダントリ:デフォルトのコンストラクタは、コードで明示的に記述しない場合にJavaコンパイラによって生成されるコンストラクタです。引数なしのコンストラクタを意味します。
John Topley、

それは「暗黙のデフォルトコンストラクタ」ではありませんか?
cretzel

2
「クラスにコンストラクターを提供する必要はありませんが、これを行うときは注意が必要です。コンパイラーは、コンストラクターのないクラスに引数なしのデフォルトコンストラクターを自動的に提供します。」java.sun.com/docs/books/tutorial/java/javaOO/constructors.html – John Topley
John Topley

回答:


184

ここで最もよく参照しているのは、ジョシュア・ブロッホの優れた著書「Effective Java」の項目19です。(第2版の項目17と第1版の項目15です。)本当に読むべきですが、要約します。

継承されたクラスとその親との相互作用は、祖先が継承元として設計されていなかった場合、意外で予測できない可能性があります。

したがって、クラスには次の2種類があります。

  1. 拡張するように設計されたクラス、およびその方法を説明するための十分なドキュメント

  2. 最終とマークされたクラス

純粋に内部コードを記述している場合、これは少しやり過ぎかもしれません。ただし、クラスファイルに5文字を追加するために必要な追加の労力はごくわずかです。内部消費のみを目的として記述している場合、将来のコーダーは常に「最終」を削除できます。「このクラスは継承を考慮して設計されていません」という警告と考えることができます。


6
私以外の誰かがコードを使用する場合、私の経験ではこれはやり過ぎではありません。Javaにデフォルトでオーバーライド可能なメソッドがある理由を私は理解していません。
Eric Weilnau、2008年

9
効果的なJavaを理解するには、Joshの設計に対する理解を理解する必要があります。彼は、[のようなもの]は、クラスインターフェイスをパブリックAPIであるかのように常に設計する必要があると言います。多くの意見では、このアプローチは通常、重すぎて柔軟性に欠けると思います。(YAGNIなど)
トムホーティン- tackline

9
いい答えだ。(スペースもあるので6文字であることを指摘するのは
仕方ありませ

36
クラスをfinalにするとテストが難しくなる(スタブ化やモック化が難しくなる)
ことに注意してください。notnoop

10
最終クラスがインターフェースに書き込んでいる場合、モッキングは問題になりません。
Fred Haslam、

26

メソッドをfinalにして、オーバーライドするクラスが他のメソッドに依存している動作を変更できないようにすることができます。コンストラクターで呼び出されるメソッドは、最終的に宣言されることが多いため、オブジェクトを作成するときに不愉快な驚きを得ることがありません。


この答えはよくわかりませんでした。よろしければ、「クラスをオーバーライドしても、他のメソッドでカウントされている動作を変更できない」とはどういう意味ですか?1つは文を理解できなかったため、もう1つはNetbeansがコンストラクターから呼び出す関数の1つをにすることを推奨しているためですfinal
ナビゲーション

1
@Navメソッドをfinalにすると、オーバーライドするクラスはそのメソッドの独自のバージョンを持つことができなくなります(またはコンパイルエラーになります)。そうすれば、そのメソッドに実装する動作は、後で誰かがクラスを拡張しても変更されないことがわかります。
トカゲに請求

19

クラスをfinalにする理由の1つは、継承よりも構成を強制したい場合です。これは、クラス間の密結合を回避するのに一般的に望ましいものです。


15

最終的な方法に進むことができる3つのユースケースがあります。

  1. 派生クラスが特定の基本クラス機能をオーバーライドしないようにするため。
  2. これはセキュリティの目的で、基本クラスがフレームワークの重要なコア機能を提供し、派生クラスがそれを変更することは想定されていません。
  3. 最終メソッドとプライベートメソッドでは仮想テーブルの概念を使用しないため、最終メソッドはインスタンスメソッドよりも高速です。したがって、可能性がある場合は、最終的な方法を使用するようにしてください。

クラスをファイナルにする目的:

そのため、どのボディもこれらのクラスを拡張してその動作を変更することはできません。

例:ラッパークラスIntegerは最終クラスです。そのクラスがfinalでない場合、Integerを自分のクラスに拡張して、整数クラスの基本的な動作を変更できます。これを回避するために、Javaはすべてのラッパークラスを最終クラスとして作成しました。


あなたの例にはあまり納得できません。その場合は、クラス全体をfinalにするのではなく、メソッドと変数をfinalにしないでください。回答を詳細に編集してください。
Rajkiran、2015年

4
すべての変数とメソッドをfinalにしても、他のクラスはクラスを継承し、機能を追加してクラスの基本的な動作を変更できます。
user1923551

3
>そのクラスがfinalでない場合、Integerを自分のクラスに拡張して、整数クラスの基本的な動作を変更できます。これが許可され、この新しいクラスが呼び出されたとしましょう。DerivedIntegerそれでも元のIntegerクラスは変更されません。また、使用する人はDerivedInteger自分の責任で変更します。そのため、なぜ問題が発生するのかわかりません。
シッダールタ2017


7

継承はチェーンソーのようなものです-非常に強力ですが、悪意のある人にとってはひどいものです。継承元のクラスを設計するか(柔軟性を制限し、時間がかかる可能性があります)、または禁止する必要があります。

有効なJava 2ndエディションのアイテム16と17、または私のブログ投稿「相続税」を参照してください。


ブログ投稿へのリンクが壊れています。また、これについてもう少し詳しく説明できると思いますか?

@MichaelT:リンクを修正しました。ありがとう-詳細については、フォローしてください:)(現時点でこれを追加する時間はありません。恐れ入ります。)
Jon Skeet

@JonSkeet、ブログ投稿へのリンクが壊れています。
Lucifer

@Kedarnath:それはそれは...私のために動作しましたしばらく壊れて、それは今... codeblog.jonskeet.ukに、正しいページを指しています
ジョンスキート

4

うーん...私は2つのことを考えることができます:

特定のセキュリティ問題を扱うクラスがあるかもしれません。それをサブクラス化し、サブクラス化されたバージョンをシステムに提供することにより、攻撃者はセキュリティ制限を回避することができます。たとえば、アプリケーションがプラグインをサポートしていて、プラグインがセキュリティ関連クラスをサブクラス化できる場合、このトリックを使用して、サブクラス化されたバージョンのプラグインを何らかの方法で密輸できます。ただし、これはSunがアプレットなどに関して対処しなければならないことであり、おそらく現実的なケースではありません。

より現実的なのは、オブジェクトが変更可能にならないようにすることです。たとえば、文字列は不変であるため、コードはそれへの参照を安全に保持できます

 String blah = someOtherString;

最初に文字列をコピーする代わりに。ただし、文字列をサブクラス化できる場合は、文字列値を変更できるメソッドを追加できます。これで、上記のように文字列をコピーするだけで文字列が同じであるというコードに依存できなくなり、代わりに、ストリング。


2

また、商用のクローズドソースクラスを作成している場合、特に機能をサポートする必要があり、人々がメソッドをオーバーライドしており、それを呼び出すと不満が出る場合は、人々が将来的に機能を変更できないようにする必要があるかもしれません。予期しない結果。


2

クラスとメソッドをfinalとしてマークすると、ランタイムが特定のオブジェクトに対して呼び出すために適切なクラスメソッドを検索する必要がないため、パフォーマンスがわずかに向上する場合があります。非最終メソッドは仮想としてマークされているので、必要に応じて適切に拡張でき、最終メソッドはクラス内で直接リンクまたはコンパイルできます。


1
現在の世代のVMは必要に応じて最適化および最適化解除できるはずなので、これは神話だと思います。私はこれがいくつかのベンチアームをしていて、それを神話として分類したのを覚えています。
Miguel Ping、

1
コード生成の違いは神話ではありませんが、おそらくパフォーマンスの向上は違います。ささやかな向上と言ったように、あるサイトでは3.5%のパフォーマンス向上と、それよりも高いパフォーマンスについて言及しましたが、ほとんどの場合、コードですべてのナチスに参加する価値はありません。
seanalltogether

1
それは神話ではありません。実際、コンパイラーはV finalメソッドのみをインライン化できます。JITがアートランタイムを「インライン化」するかどうかはわかりませんが、後で派生クラスをロードする可能性があるため、そうすることはできません。
ウリ

1
マイクロベンチマーク==塩粒。そうは言っても、10Bサイクルのウォームアップ後、10Bを超える平均呼び出しは、私のラップトップ上のEclipseで本質的に違いを示しません。文字列を返す2つのメソッド、最終は6.9643us、非最終は6.9641usでした。そのデルタはバックグラウンドノイズです。
joel.neely 2009

1

自分や他の人を混乱させる可能性のあることをするのをやめるため。定数または計算が定義されている物理ライブラリを想像してみてください。finalキーワードを使用しないと、誰かがやって来て、絶対に変更してはならない基本的な計算または定数を再定義する可能性があります。


2
その議論について私が理解していないことがある-誰かがそれらの計算/定数を変更してはならないのに変更した場合-その100%のせいで失敗はありませんか?言い換えれば、この変更はどのようなメリットがあるのでしょうか。
matt b

はい、それは技術的には「彼ら」の責任ですが、変更を認識させられていないライブラリを使用している他の誰かが混乱を招く可能性があると想像してください。人々が基本的な機能を変更するのをやめるために、Java Baseクラスの多くがfinalとマークされたのはそのためだといつも思っていました。
Anson Smith、

@matt b-変更を加えた開発者が故意にそうしたか、それが彼らのせいだと気にかけていたと想定しているようです。私以外の誰かが私のコードを使用する場合は、変更するつもりがない限り、それを最終的なものとしてマークします。
Eric Weilnau、2008年

これは実際に尋ねられたものとは異なる「最終」の使用法です。
DJClayworth 2008年

この回答は、個々のフィールドの継承と可変性を、サブクラスを作成する機能と混同しているようです。継承を禁止することなく、個々のフィールドとメソッドを最終(それらの再定義を防ぐ)としてマークできます。
joel.neely 2009

0

クラスをオーバーライドしても動作が変わらないように、メソッドをfinalにしたいとします。動作を変更できるようにするには、メソッドをパブリックにします。パブリックメソッドをオーバーライドすると、変更できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.