回答:
まず、この記事をお勧めします:Java:最終クラスを作成するタイミング
彼らがそうするなら、いつそれを使うのか私はそれをよりよく理解し、いつ使うべきかを知ることができます。
final
クラスは、単にクラスで拡張することはできません。
(クラスのオブジェクトへのすべての参照が、として宣言されているかのように動作することを意味するわけではありませんfinal
。)
クラスをfinalとして宣言すると便利な場合は、この質問の回答で説明されています。
Javaがオブジェクト指向で、クラスを宣言する場合
final
、オブジェクトの特性を持つクラスの考えを止めませんか?
ある意味ではそうです。
クラスを最終としてマークすることにより、コードのその部分の言語の強力で柔軟な機能を無効にします。ただし、一部のクラスは、サブクラス化を適切に考慮に入れるように設計してはなりません(場合によってはできません)。これらの場合、OOPを制限しますが、クラスを最終としてマークすることは理にかなっています。(ただし、最終クラスはまだ非最終クラスを拡張できることに注意してください。)
Javaでは、final
修飾子のあるアイテムは変更できません。
これには、最終クラス、最終変数、および最終メソッドが含まれます。
final
修飾子のあるアイテムは変更できません!」というステートメントは、あまりにも明確であり、実際には完全に正しいわけではありません。Grady Boochが言うように、「オブジェクトには状態、動作、およびアイデンティティがあります」。参照が最終としてマークされた後でオブジェクトのIDを変更することはできませんが、新しい値をその非フィールドに割り当てることでその状態を変更する機会がありfinal
ます(もちろん、それが提供されている場合)。 Oracle Java認定(1Z0-808など)の取得を計画している場合は、この点に
セキュリティ上の理由から、クラスの継承を防止する場合に、finalが重要になる1つのシナリオ。これにより、実行中のコードが誰かによって上書きされないようにすることができます。
別のシナリオは最適化です。Javaコンパイラーが最終クラスからのいくつかの関数呼び出しをインライン化することを覚えているようです。したがって、をa.x()
宣言してaを宣言するとfinal
、コンパイル時にコードがどのようになるかがわかり、呼び出し元の関数にインライン化できます。これが実際に行われたかどうかはわかりませんが、最終的には可能です。
最良の例は
public final class String
これは不変のクラスであり、拡張できません。もちろん、クラスをfinalにするだけでは不変です。
関連する読み物:ボブ・マーティンによる開閉原理。
主な引用:
ソフトウェアエンティティ(クラス、モジュール、関数など)は、拡張のために開いている必要がありますが、変更のために閉じている必要があります。
final
キーワードは、メソッドやクラスに使われているかどうか、Javaでこれを強制するための手段です。
final
を宣言すると、クラスはオープンではなく拡張のためにクローズされますか?それとも私は文字通りそれを取っていますか?
final
クラス/メソッド宣言での使用は、実装コードを変更のためにクローズし、継承によって拡張のためにオープンしたい場合には意味がありません。
キーワードfinal
自体は、何かが最終的なものであり、いかなる方法でも変更されるべきではないことを意味します。クラスがマークされている場合、final
それを拡張またはサブクラス化することはできません。しかし問題は、なぜクラスをマークするのかということfinal
です。IMOにはさまざまな理由があります。
クラスをマークfinal
することで効率が向上すると聞いたことがありますが、率直に言って、この議論が大きな重みをもたらすとは思えませんでした。
Javaがオブジェクト指向であり、クラスをfinalと宣言した場合、クラスはオブジェクトの特性を持つという考えを止めませんか?
おそらくそうですが、それが意図された目的である場合もあります。拡張するこのクラスの機能を犠牲にすることで、セキュリティなどの大きな利点を達成するために時々それを行います。ただし、最終クラスは、必要に応じて1つのクラスを拡張できます。
サイドノートでは、我々はすべき相続上の組成を好むとfinal
キーワード実際にこの原則を施行するのに役立ちます。
final class
新しいメソッドを追加するときにパブリックAPIが壊れることを回避できます
Base
クラスのバージョン1で次のようにするとします。
public class Base {}
そしてクライアントは:
class Derived extends Base {
public int method() { return 1; }
}
次に、バージョン2でmethod
メソッドを追加したい場合Base
:
class Base {
public String method() { return null; }
}
クライアントコードが壊れます。
final class Base
代わりに使用した場合、クライアントは継承できず、メソッドを追加してもAPIは壊れません。
クラスがとマークされている場合はfinal
、クラスの構造を外部から変更することはできません。これが最も目立つのは、従来の多態的な継承を行っているときですclass B extends A
。これは基本的に、コードの一部を(ある程度)保護する方法です。
明確にするために、クラスをfinal
マークしてもフィールドはマークさfinal
れないため、オブジェクトのプロパティは保護されず、実際のクラス構造が保護されます。
最終クラスの問題に対処するには:
クラスをファイナルにする方法は2つあります。1つ目は、クラス宣言でキーワードfinalを使用することです。
public final class SomeClass {
// . . . Class contents
}
クラスをfinalにする2つ目の方法は、すべてのコンストラクターをプライベートとして宣言することです。
public class SomeClass {
public final static SOME_INSTANCE = new SomeClass(5);
private SomeClass(final int value) {
}
最終とマークすることで、実際に最終であることが判明した場合に、このTestクラスを確認するためのトラブルを回避できます。一見して公共のように見えます。
public class Test{
private Test(Class beanClass, Class stopClass, int flags)
throws Exception{
// . . . snip . . .
}
}
残念ながら、クラスの唯一のコンストラクターはプライベートなので、このクラスを拡張することは不可能です。Testクラスの場合、クラスがfinalである必要はありません。Testクラスは、暗黙の最終クラスが問題を引き起こす可能性のある良い例です。
そのため、コンストラクタをプライベートにしてクラスを暗黙的にfinalにする場合は、finalとマークする必要があります。
はい。ただし、セキュリティまたは速度の理由から、これが必要になる場合があります。C ++でも行われます。それはプログラムには適用できないかもしれませんが、フレームワークにも適用できます。 http://www.glenmccl.com/perfj_025.htm
Javaのfinalキーワードでは、以下の場合に使用されます。
Javaでは、final変数は再割り当てできません。final クラスは拡張できず、finalメソッドはオーバーライドできません。
最終クラスは拡張できません。したがって、クラスが特定の方法で動作し、誰かがメソッドをオーバーライドしないようにしたい場合は(おそらく効率が低く、より悪意のあるコードで)、クラス全体を不要な最終メソッドまたは特定のメソッドとして宣言できますかわった。
クラスを宣言しても、クラスのインスタンス化が妨げられるわけではないので、クラスがオブジェクトの特性を持つことを停止するわけではありません。クラスで宣言されているとおりにメソッドに固執する必要があるだけです。
FINALを「ラインの終わり」と考えてください-その男はもう子孫を生み出すことができません。したがって、このように見ると、クラスの「行末」マーカーにフラグを立てる必要がある、実際に遭遇するシナリオがたくさんあります。これはドメイン駆動設計です。ドメインで特定のENTITY(クラス)がサブクラスを作成できないことを要求する場合は、FINALとしてマークしてください。
「最終としてタグ付けする必要がある」クラスの継承を妨げるものは何もないことに注意してください。しかし、それは一般に「継承の乱用」として分類され、ほとんどの場合、クラスの基本クラスから関数を継承したいために行われます。
最善のアプローチは、ドメインを調べて、設計の決定を決定させることです。
Android Looperクラスは、この良い実用例です。 http://developer.android.com/reference/android/os/Looper.html
Looperクラスは、他のクラスによってオーバーライドされることを意図していない特定の機能を提供します。したがって、ここにはサブクラスはありません。
Employee
メソッドを持つクラスがあるとしましょうgreet
。ときにgreet
メソッドが呼び出され、それは単純に印刷しますHello everyone!
。これがメソッドの予想される動作ですgreet
public class Employee {
void greet() {
System.out.println("Hello everyone!");
}
}
次に、以下に示すようにメソッドをGrumpyEmployee
サブクラス化Employee
してオーバーライドしますgreet
。
public class GrumpyEmployee extends Employee {
@Override
void greet() {
System.out.println("Get lost!");
}
}
以下のコードでsayHello
メソッドを見てみましょう。これは、かかるEmployee
パラメータとしてインスタンスをし、それが言うことを期待して挨拶メソッドを呼び出しますHello everyone!
。しかし、私たちが得ることですGet lost!
。この動作の変化は、Employee grumpyEmployee = new GrumpyEmployee();
public class TestFinal {
static Employee grumpyEmployee = new GrumpyEmployee();
public static void main(String[] args) {
TestFinal testFinal = new TestFinal();
testFinal.sayHello(grumpyEmployee);
}
private void sayHello(Employee employee) {
employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
}
}
クラスが作成された場合、この状況は回避できます。Classがとして宣言されていない場合に、生意気なプログラマーが引き起こし得る混乱の量を想像してみてください。Employee
final
String
final
オブジェクト指向は継承についてではなく、カプセル化についてです。そして継承はカプセル化を壊します。
クラスのファイナルを宣言することは、多くの場合に完全に理にかなっています。色や金額などの「価値」を表すオブジェクトはすべて最終的なものになる可能性があります。彼らは独力で立ちます。
ライブラリを作成する場合は、派生させるために明示的にインデントしない限り、クラスをfinalにします。そうしないと、人々がクラスを派生させてメソッドをオーバーライドし、仮定/不変条件を破る可能性があります。これは、セキュリティにも影響を与える可能性があります。
「Effective Java」のJoshua Blochは、継承のために明示的に設計するか、それを禁止することを推奨しており、継承のための設計はそれほど簡単ではないと指摘しました。