CodeGym /Java Blog /ランダム /Java の Final キーワードについて話しましょう
John Squirrels
レベル 41
San Francisco

Java の Final キーワードについて話しましょう

ランダム グループに公開済み
Java には「final」というキーワードがあります。クラス、メソッド、変数 (メソッドのパラメーターを含む) に適用できます。クラスの場合、final キーワードは、クラスがサブクラスを持つことができないこと、つまり継承が禁止されていることを意味します。これは、不変 (変更不可能) オブジェクトを作成する場合に便利です。たとえば、String クラスは Final として宣言されます。

    public final class String {
    }
    
    class SubString extends String { // Compilation error
    }
また、final 修飾子は抽象クラス (abstract キーワードを持つクラス) には適用できないことにも注意してください。これらは相互に排他的な概念であるためです。Final メソッドの場合、修飾子はメソッドがサブクラスでオーバーライドできないことを意味します。これは、元の実装の変更を防ぎたい場合に便利です。

    public class SuperClass {
        public final void printReport() {
            System.out.println("Report");
        }
    }

    class SubClass extends SuperClass { 
        public void printReport() { //Compilation error
            System.out.println("MyReport");
        }
    }
プリミティブ型の変数の場合、final キーワードは、一度割り当てられた値は変更できないことを意味します。参照変数の場合、オブジェクトが割り当てられた後は、そのオブジェクトへの参照を変更できないことを意味します。これは重要!参照は変更できませんが、オブジェクトの状態は変更できます。Java 8 では、事実上最終的な新しい概念が導入されました。これは変数 (メソッド パラメーターを含む) にのみ適用されます。結論としては、final キーワードが明らかに存在しないにもかかわらず、変数の値は初期化後に変更されないということです。言い換えれば、final キーワードはコンパイル エラーなしでそのような変数に適用できます。事実上、final 変数は、ローカル クラス (ローカル内部クラス)、匿名クラス (匿名内部クラス)、およびストリーム (ストリーム API) 内で使用できます。

        public void someMethod() {
            // In the example below, both a and b are effectively final, since they are assigned values only once:
            int a = 1;
            int b;
            if (a == 2) b = 3;
            else b = 4;
            // c is NOT effectively final since its value changes
            int c = 10;
            c++;
            
            Stream.of(1, 2).forEach(s-> System.out.println(s + a)); // OK
            Stream.of(1, 2).forEach(s-> System.out.println(s + c)); // Compilation error
        }
さて、ちょっとインタビューしてみましょう。結局のところ、CodeGym コースを完了する目標は、Java 開発者になって、面白くて高収入の仕事を見つけることです。それでは、始めましょう。
  1. Final と宣言された配列については何が言えるでしょうか?

  2. String クラスは不変であることがわかっています。このクラスは Final として宣言されています。文字列値は、キーワード Final でマークされた char 配列に格納されます。


public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

String オブジェクトの値を (オブジェクトへの参照を変更せずに) 置き換えることはできますか? これらは実際の面接の質問です。そして実際にやってみると、多くの受験者が正しく答えていないことがわかります。特に参照変数の場合、final キーワードがどのように使用されるかを理解することが非常に重要です。あなたがこれについて熟考しているときに、CodeGym チームに小さなお願いをします。テキスト エディタに、クリックしたときに内容を表示/非表示にできるブロックを追加できるようにしてください。 答え:
  1. 配列はオブジェクトであるため、final キーワードは、配列への参照が割り当てられると、その参照を変更できないことを意味します。つまり、オブジェクトの状態を変更することはできます。

    
            final int[] array = {1, 2, 3, 4, 5};
            array[0] = 9;	 // OK, because we're changing the contents of the array: {9, 2, 3, 4, 5}
            array = new int[5]; // Compilation error
    
  2. はい、できます。重要なことは、厄介なfinalキーワードがオブジェクトで使用される場合に何を意味するのかを理解することです。Reflection API を使用して値を置換できます。


import java.lang.reflect.Field;

class B {
    public static void main(String[] args) throws Exception {
        String value = "Old value";
        System.out.println(value);

        // Get the String class's value field
        Field field = value.getClass().getDeclaredField("value");
        // Make it mutable
        field.setAccessible(true);
        // Set a new value
        field.set(value, "CodeGym".toCharArray());

        System.out.println(value);

        /* Output:
         * Old value
         * CodeGym
         */
    }
}
この方法でプリミティブ型の最終変数を変更しようとしても、何も起こらないことに注意してください。自分自身を納得させることをお勧めします。たとえば、final int フィールドを含む Java クラスを作成し、Reflection API を使用してその値を変更してみてください。皆さんお元気で!
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION