読者です 読者をやめる 読者になる 読者になる

ソフトウェアエンジニア現役続行

雑多なことを綴ります

定数変数(constant variable)の値を変更するときの注意点

Java

プログラミング言語Java - 第4版の第2刷のP.40で定数変数(constant variable)について以下のように説明しています。

コンパイル時に値が決定できる定数式で初期化された基本データ型、あるいは、nullではない定数式で初期化されたString型のfinalフィールドは、定数変数(constant variable)と呼ばれます。コンパイラーは、定数変数をフィールドとしてではなく、値として扱いますので、定数変数は特殊です。コードが定数変数を参照していると、コンパイラーは、オブジェクトからそのフィールドの値をロードするバイトコードを生成するのではなく、単にバイトコードに値を直接挿入します。これは、有用な最適化ですが、finalフィールドの値を変更する必要がある場合には、そのフィールドを参照しているすべてのコードを再コンパイルしなければならないことになります。

ぱっと読んだだけだと理解できませんでしたが、よく読めば理解できました。その理解が正しいのか、実際にコードを書いて確かめてみました。


以下のようにHogeクラスとFugaクラスを作成しました。

Hoge.java

public class Hoge {
  public static void main (String[] args) {
    System.out.println (Fuga.CONSTANT_INT);
    System.out.println (Fuga.CONSTANT_INTEGER);
  }
}

Fuga.java(変更前)

public class Fuga {
  static final int CONSTANT_INT = 5;
  static final Integer CONSTANT_INTEGER = new Integer(5);
}

このように、HogeクラスがFugaクラスのCONSTANT_INTとCONSTANT_INTEGERを参照しています。これをコンパイルして実行します。

[Oswald@localhost java]$ javac Hoge.java Fuga.java
[Oswald@localhost java]$ java Hoge
5
5

これは予想通りの結果です。


次に、Fugaクラスの変数を変更します。


Fuga.java(変更後)

public class Fuga {
  static final int CONSTANT_INT = 4;
  static final Integer CONSTANT_INTEGER = new Integer(4);
}

2つの変数の値を5から4に変更しました。そして、変更したFugaクラスだけコンパイルしなおして実行します。

[Oswald@localhost java]$ javac Fuga.java
[Oswald@localhost java]$ java Hoge
5
4

これは予想通りの結果ではありません。変数を4に変更したにもかかわらず、5が出力されてしまいました。


これはHogeクラスがコンパイルされたときに、参照しているFugaクラスの変数がintのような基本データ型のfinalフィールド(これを定数変数(constant variable)という)だと「Fuga.CONSTANT_INTは5」と認識され、参照しているFugaクラスの変数がInteger型だと「Fuga.CONSTANT_INTEGERはFugaクラスで定義された値」と認識されるからです。


つまり、HogeクラスはFuga.CONSTANT_INTがまだ5だと思い込んでいます。Fuga.CONSTANT_INTEGERは実行時にFugaクラスにアクセスするため、実行時に4だと認識することが出来ます。


これを解決するためには、定数変数を参照しているクラスもコンパイルしなおします。

[Oswald@localhost java]$ javac Hoge.java
[Oswald@localhost java]$ java Hoge
4
4