Code Fix

上級

cannot assign a value to final variable の原因と直し方

finalが固定するのは参照であって中身ではありません。final / static final の違いを整理します。

エラーメッセージの読み方

Main.java:4: error: cannot assign a value to final variable LIMIT
Main.java
ファイル名
4
行番号 — javacが異常に気づいた位置。原因の行とは限りません
error
種別 — コンパイルエラー。実行まで到達しません
cannot assign a value to final variable LIMIT
内容 — javacが期待していたもの、または見つけられなかったもの

このエラーが出る典型パターン

パターン1

 1  public class Main {
 2      public static void main(String[] args) {
 3          final int LIMIT = 10;
 4          LIMIT = 20; 
                  ^
 5          System.out.println(LIMIT);
 6      }
 7  }
Main.java:4: error: cannot assign a value to final variable LIMIT

finalは1度しか代入できません。定数として使うなら宣言と同時に初期化し、以後は再代入しません。

直し方: LIMIT = 20; を (空) にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  import java.util.ArrayList;
 2  import java.util.List;
 3  
 4  public class Main {
 5      public static void main(String[] args) {
 6          final List<String> list = new ArrayList<>();
 7          list = new ArrayList<>();
                        ^
 8          System.out.println(list);
 9      }
10  }
Main.java:7: error: cannot assign a value to final variable list

finalが固定するのは参照であって中身ではありません。要素の追加や削除は自由にできます。

直し方: list = new ArrayList<>() を list.add("a") にします。

この問題を解いてみる →

パターン3

 1  public class Main {
 2      static final int ID;
              ^
 3      Main(int id) { this.ID = id; }
 4      public static void main(String[] args) {
 5          System.out.println(new Main(7).ID);
 6      }
 7  }
Main.java:3: error: cannot assign a value to final variable ID

static finalはクラス単位の定数なのでコンストラクタで代入できません。インスタンスごとに固定するならfinalのみです。

直し方: static final を final にします。

この問題を解いてみる →

パターン4

 1  public class Main {
 2      static int square(final int n) {
 3          n = n + 1;
                 ^
 4          return n * n;
 5      }
 6      public static void main(String[] args) {
 7          System.out.println(square(4));
 8      }
 9  }
Main.java:3: error: final parameter n may not be assigned

finalが付いた引数は再代入できません。同じ意味の別変数が必要なら新しいローカル変数を宣言します。

直し方: n = n + 1; を (空) にします。

この問題を解いてみる →

パターン5

 1  public class Main {
 2      final int id;
 3      Main(int id) { this.id = id; }
 4      Main() {              }
                       ^
 5      public static void main(String[] args) {
 6          System.out.println(new Main().id);
 7      }
 8  }
Main.java:4: error: variable id might not have been initialized

finalフィールドはすべてのコンストラクタで必ず初期化しなければなりません。1つでも代入し忘れると、そのコンストラクタでコンパイルエラーになります。

直し方: (空) を this.id = 0; にします。

この問題を解いてみる →

よくある誤解

finalなListの中身は変えられます。本当に不変にしたいなら List.of を使ってください。

実務での勘所

finalが保証するのは「この変数に別のオブジェクトを再代入できない」ことだけで、「そのオブジェクトの中身が変更できない」こととは別の話です。真に中身も変更できないイミュータブルなオブジェクトにしたい場合は、finalを付けることに加えて、そのクラス自体が状態を変更する手段(setterなど)を持たない設計にする必要があります。StringやIntegerのようなラッパー型が安全に共有できるのは、finalだからではなく、クラス自体がイミュータブルに設計されているためです。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)