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プロジェクト]にあります。