Javaの「最終」キーワード
1. 概要
継承により既存のコードを再利用できますが、さまざまな理由でset limitations on extensibilityを使用する必要がある場合があります。 finalキーワードを使用すると、まさにそれを実行できます。
このチュートリアルでは、finalキーワードがクラス、メソッド、および変数に対して何を意味するかを見ていきます。
2. Finalクラス
Classes marked as final can’t be extended. Javaコアライブラリのコードを見ると、そこに多くのfinalクラスがあります。 一例はStringクラスです。
Stringクラスを拡張し、そのメソッドのいずれかをオーバーライドして、すべてのStringインスタンスを特定のStringサブクラスのインスタンスに置き換えることができる場合の状況を考慮してください。
Stringオブジェクトに対する操作の結果は、予測できなくなります。 そして、Stringクラスがどこでも使用されていることを考えると、それは受け入れられません。 そのため、Stringクラスはfinalとしてマークされています。
finalクラスから継承しようとすると、コンパイラエラーが発生します。 これを示すために、finalクラスCatを作成しましょう。
public final class Cat {
private int weight;
// standard getter and setter
}
それを拡張してみましょう:
public class BlackCat extends Cat {
}
コンパイラエラーが表示されます。
The type BlackCat cannot subclass the final class Cat
the final keyword in a class declaration doesn’t mean that the objects of this class are immutableに注意してください。 Catオブジェクトのフィールドは自由に変更できます。
Cat cat = new Cat();
cat.setWeight(1);
assertEquals(1, cat.getWeight());
拡張することはできません。
優れた設計のルールに厳密に従う場合は、安全上の理由から、クラスを注意深く作成して文書化するか、finalと宣言する必要があります。 ただし、finalクラスを作成するときは注意が必要です。
クラスをfinalにすると、他のプログラマーがそれを改善できないことに注意してください。 クラスを使用していて、そのソースコードがなく、1つのメソッドに問題があるとします。
クラスがfinal,の場合、メソッドをオーバーライドして問題を修正するためにクラスを拡張することはできません。 言い換えれば、オブジェクト指向プログラミングの利点の1つである拡張性を失います。
3. Finalメソッド
Methods marked as final cannot be overridden.クラスを設計し、メソッドをオーバーライドしてはならないと感じた場合、このメソッドをfinalにすることができます。 また、Javaコアライブラリには多くのfinalメソッドがあります。
クラス拡張を完全に禁止する必要はなく、一部のメソッドのオーバーライドのみを防止する必要がある場合があります。 この良い例は、Threadクラスです。 拡張して、カスタムスレッドクラスを作成することは合法です。 ただし、そのisAlive()メソッドはfinalです。
このメソッドは、スレッドが生きているかどうかをチェックします。 多くの理由により、isAlive()メソッドを正しくオーバーライドすることは不可能です。 それらの1つは、このメソッドがネイティブであることです。 ネイティブコードは別のプログラミング言語で実装されており、多くの場合、それが実行されているオペレーティングシステムとハードウェアに固有です。
Dogクラスを作成し、そのsound()メソッドをfinalにしましょう。
public class Dog {
public final void sound() {
// ...
}
}
それでは、Dogクラスを拡張して、そのsound()メソッドをオーバーライドしてみましょう。
public class BlackDog extends Dog {
public void sound() {
}
}
コンパイラエラーが表示されます。
- overrides
com.example.finalkeyword.Dog.sound
- Cannot override the final method from Dog
sound() method is final and can’t be overridden
クラスの一部のメソッドが他のメソッドによって呼び出される場合は、呼び出されるメソッドをfinalにすることを検討する必要があります。 そうでない場合、それらをオーバーライドすると、呼び出し元の作業に影響を与え、驚くべき結果を引き起こす可能性があります。
コンストラクターが他のメソッドを呼び出す場合、上記の理由から、通常、これらのメソッドをfinalと宣言する必要があります。
クラスfinalのすべてのメソッドを作成することと、クラス自体をfinalとしてマークすることの違いは何ですか? 最初のケースでは、クラスを拡張し、新しいメソッドを追加できます。
2番目の場合、これはできません。
4. Final変数
Variables marked as final can’t be reassigned.final変数が初期化されると、それを変更することはできません。
4.1. Finalプリミティブ変数
プリミティブfinal変数i,を宣言してから、それに1を割り当てましょう。
そして、値2を割り当ててみましょう:
public void whenFinalVariableAssign_thenOnlyOnce() {
final int i = 1;
//...
i=2;
}
コンパイラは言う:
The final local variable i may already have been assigned
4.2. Final参照変数
final参照変数がある場合、それを再割り当てすることもできません。 しかし、this doesn’t mean that the object it refers to is immutable。 このオブジェクトのプロパティは自由に変更できます。
これを示すために、final参照変数catを宣言し、初期化します。
final Cat cat = new Cat();
再割り当てを試みると、コンパイラエラーが表示されます。
The final local variable cat cannot be assigned. It must be blank and not using a compound assignment
ただし、Catインスタンスのプロパティは変更できます。
cat.setWeight(5);
assertEquals(5, cat.getWeight());
4.3. Finalフィールド
Final fields can be either constants or write-once fields.それらを区別するために、質問をする必要があります—オブジェクトをシリアル化する場合、このフィールドを含めますか? いいえの場合、オブジェクトの一部ではなく、定数です。
命名規則に従って、クラス定数は大文字で、下線(â_œ)文字で区切られたコンポーネントである必要があることに注意してください。
static final int MAX_WIDTH = 999;
any final field must be initialized before the constructor completesに注意してください。
static finalフィールドの場合、これはそれらを初期化できることを意味します。
-
上記の例に示すように宣言時に
-
静的初期化ブロック内
たとえば、finalフィールドの場合、これはそれらを初期化できることを意味します。
-
宣言時に
-
インスタンス初期化ブロック内
-
コンストラクタ内
そうしないと、コンパイラーはエラーを出します。
4.4. Final引数
finalキーワードは、メソッド引数の前に置くこともできます。 A final argument can’t be changed inside a method:
public void methodWithFinalArguments(final int x) {
x=1;
}
上記の割り当てにより、コンパイラエラーが発生します。
The final local variable x cannot be assigned. It must be blank and not using a compound assignment
5. 結論
この記事では、finalキーワードがクラス、メソッド、および変数に対して何を意味するかを学びました。 内部コードではfinalキーワードを頻繁に使用しない場合がありますが、これは優れた設計ソリューションである可能性があります。
いつものように、この記事の完全なコードはGitHub projectにあります。