Javaの“ final”キーワード
1概要
継承によって既存のコードを再利用できるようになりますが、さまざまな理由で** 拡張性に制限を設ける必要がある場合があります。
final
キーワードを使用すると、それを正確に実行できます。
このチュートリアルでは、
final
キーワードがクラス、メソッド、および変数に対して何を意味するのかを見ていきます。
2 __最終クラス
-
final
とマークされたクラスは拡張できません** 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
-
クラス宣言内の
final
キーワードは、このクラスのオブジェクトが不変であるという意味ではありません。
Cat
オブジェクトのフィールドは自由に変更できます。
Cat cat = new Cat();
cat.setWeight(1);
assertEquals(1, cat.getWeight());
拡張することはできません。
私たちがグッドデザインの規則に厳密に従うならば、安全のためにクラスを慎重に作成し文書化するか、あるいはそれを最終的に宣言するべきです。
ただし、最終クラスを作成するときには注意が必要です。
クラスを
final
にするということは、他のプログラマがそれを改善できないことを意味することに注意してください。クラスを使用していて、そのためのソースコードがないことを想像してみてください。1つのメソッドに問題があります。
クラスが
finalの場合、
メソッドをオーバーライドして問題を解決するようにクラスを拡張することはできません。つまり、オブジェクト指向プログラミングの利点の1つである拡張性を失います。
3最後のメソッド
-
final
とマークされたメソッドはオーバーライドできません** クラスをデザインしてメソッドをオーバーライドするべきではないと感じるとき、このメソッドを
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.baeldung.finalkeyword.Dog.sound
- Cannot override the final method from Dog
sound() method is final and can’t be overridden
私たちのクラスのいくつかのメソッドが他のメソッドによって呼び出される場合、呼び出されたメソッドを
最終
にすることを検討する必要があります。さもなければ、それらを無効にすることは呼び出し側の仕事に影響を及ぼし、驚くべき結果を引き起こす可能性があります。
私たちのコンストラクタが他のメソッドを呼び出す場合、上記の理由で一般的にこれらのメソッドを
final
と宣言するべきです。
クラスのすべてのメソッドを
final
にすることと、クラス自体を
final
にマークすることの違いは何ですか?最初のケースでは、クラスを拡張して新しいメソッドを追加することができます。
2番目のケースでは、これができません。
4最終変数
-
final
とマークされた変数は再割り当てできません。
4.1.
最終
プリミティブ変数
プリミティブ
最終
変数__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
参照変数
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
フィールドは、定数または追記型フィールドのいずれかになります。** 区別するために、質問をする必要があります。いいえの場合、それはオブジェクトの一部ではなく、定数です。
命名規則に従って、クラス定数は大文字にし、コンポーネントはアンダースコア( “__”)文字で区切ります。
static final int MAX__WIDTH = 999;
コンストラクタが完了する前に、どの
final
フィールドも初期化しなければならないことに注意してください。
静的最終フィールドの場合、これは初期化できることを意味します。
-
上記の例のように宣言時
-
スタティックイニシャライザブロック内
例えば
final
フィールドの場合、これは初期化できることを意味します。
-
宣言時に
-
インスタンス初期化子ブロック内
-
コンストラクタ内
そうでなければ、コンパイラはエラーを出します。
4.4.
最終
引数
final
キーワードもメソッドの引数の前に置くことができます。
final
引数はメソッド内では変更できません
:
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
キーワードを使用することはあまりないかもしれませんが、それは優れた設計ソリューションになるかもしれません。
いつものように、この記事の完全なコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-lang-oop[GitHubプロジェクト]にあります。