Search on the blog

2013年9月9日月曜日

Javaのfinalについて

 Javaのfinalについて勘違いしていたので、メモしておきます。「finalを付けたら変数の中身が変わらない」と思っていましたが、実際は少し違うようです。以下のサンプルソースを見てください。runメソッドの中でfinal宣言した変数を使って何かをしています。このうちコンパイルエラーになるのはどれでしょうか?
package com.kenjih.test;

import java.util.HashMap;
import java.util.Map;

public class Clazz {
    static final int num;
    static final String str;
    static final Map<String, String> map;
    static final Day weekDay;
    
    public enum Day {
        SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY 
    }
    
    static {
        str = "hogehoge";
        map = new HashMap<String, String>() {
            {
                put("red", "apple");
                put("yellow", "banana");
            }
        };
        num = 10;
        weekDay = Day.MONDAY;
    }
    
    public void run() {
        str = "fugafuga";                           // 1
        map.put("green", "kiwi");                   // 2
        map = new HashMap<String, String>();  // 3
        num = 12;                                   // 4
        weekDay = Day.MONDAY;                       // 5
    }
    
    public static void main(String[] args) {
        new Clazz().run();
    }
}

全部エラーだろと思ってました。

正解は、
「2.はエラーにならない。それ以外はコンパイルエラー」です。

1. はstrを違う文字列に変更しようとしています。strはfinalなのでエラーです。
厳密に言うと、参照型の変数strが指しているオブジェクトの実体を変更しようとしたためエラーになります。オブジェクトの中身を変更しようとしたからエラーになったわけではないということに注意です(String型はimmutable)。

2. はエラーになりません。参照型の変数mapが指す実体は変わらないからです。参照先のオブジェクトに副作用のある操作が行われても、参照しているインスタンスが変わらなければエラーにはなりません。

3. は新しいオブジェクトをインスタンス化して、それをmapに代入しようとしています。この場合はmapの参照先が変わることになるのでエラーになります。

4.はprimitive型の例です。値を変えようとするとエラーになります。

5.は列挙型です。同様に値を変えようとするとエラーになります。

0 件のコメント:

コメントを投稿