かとじゅんの技術日誌

技術の話をするところ

シングルトンパターンの遅延初期化をスレッドセーフにするには

前回、前々回と割とヘビーな仕様の話だったので、今回は若干実用的なネタとして、シングルトンパターンの遅延初期化をメモリモデルの視点から、どのようにすればスレッドセーフになるか考えてみたいと思います。

そのシングルトンの遅延初期化はスレッドセーフか

以下のようなシングルトンパターンは日常的に使うデザインパターンの一つだと思います。
ただ、今回はSingletonクラスのgetInstanceメソッドに、わざとロックを掛けずに実装してみました。この場合にどういうことが起こりうるか考えてみたいと思います。

public class Singleton {
	private static Singleton instance;

	public static Singleton getInstance() {
		// バリアがない
		if (instance == null) { // Normal Load (1)
			// バリアがない
			instance = new Singleton(); // Normal Store (2)
			// バリアがない
		}
		// バリアがない
		return instance; // Normal Load(3)
                // バリアがない
	}

	public static void main(String[] args) {
		Thread aThread = new Thread(new Runnable() { // スレッドA
					@Override
					public void run() {
						Singleton s = Singleton.getInstance();
					}
				});
		Thread bThread = new Thread(new Runnable() { // スレッドB
					@Override
					public void run() {
						Singleton s = Singleton.getInstance();
					}
				});
		aThread.start();
		bThread.start();
		try {
			aThread.join();
			bThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private Singleton() {
	}

}

とりあえず、mainメソッドを見てください。二つのスレッドからgetInstanceメソッドを呼ぶ前提で、そのgetInstanceメソッド内部では、変数への読み書きは、Normal Load, Normal Store です。リオーダーするとセマンティクスが変わるので、おそらくリオーダーされないと思うのですが、いずれにしてもNormalなので、メモリバリア命令は挿入されないはずです。ロックがないのでスレッドが割り込んで初期化が複数回行われる可能性があります。さらにあるスレッドで初期化したのに、他のスレッドではその初期化が見えないという問題も起こります。これはメモリバリアがないために起こる問題です。
なぜかというと、instanceフィールドへの読み書きは、スレッドごとのローカルキャッシュなどに対して行われるはずで、メインメモリに対して読み書きできる保証がないということが理由。
つまり、スレッドAで実行された書込みがスレッドBで読み込める保証がありません。また、その逆も同じで、スレッドBで実行された書込みがスレッドAで読み込める保証がありません。 ステートを他のスレッドに伝えるような通信効果を求めるならば、JVMがバリア命令を挿入するようなコードを書く必要があります。
Effective Java第二版の「項目66 共有された可変データへのアクセスを同期する」にも同様のことが書かれていますが、この説明だけではなぜそうなるのか、根拠がわからないので以下にメモリモデルの視点から対策を考えてみました。

スレッドセーフ対策案

対策1:volatileフィールドを使う

volatileフィールドを利用した場合は、以下。
随所にバリアが挿入されているので、メインメモリへの読み書きは反映されそうですが、アトミック性が保証されていないので、Volatile Load (1)とVolatile Store (2)の間にスレッドが割り込んで、初期化が複数回実行される可能性があります。これではスレッドセーフとは言えないので対策としては相応しくないです。
(*)はThe JSR-133 CookbookのInserting Barriers(バリアの挿入)にある特別なルールに基づくバリア命令を想定しています。*1

	private static volatile Singleton instance;

	public static Singleton getInstance() {
		// LoadLoadバリア (*)
		// LoadStoreバリア (*)
		if (instance == null) { // Volatile Load (1)
			// LoadStoreバリア
			// StoreStoreバリア (*)
			instance = new Singleton(); // Volatile Store (2)
			// StoreLoadバリア (*)
		}
		// LoadLoadバリア (*)
		// LoadStoreバリア (*)
		return instance; // Volatile Load(3)
	}
対策2:固有ロック(synchronized)を使う

ロックをかけた場合は、EnterLoadバリアとStoreExitバリアが以下のように挿入されます。
最初にロックを取得したスレッドAで行ったinstanceフィールドへの書込みは、StoreExitバリアによりメインメモリにストアされます。次にロックを取得したスレッドBでは、先行発生の仕様によりinstanceフィールドの最新の値をメインメモリからロードすることができます。
スレッドが頻繁にgetInstnaceメソッドを呼び出す場合はロックの争奪が起こりパフォーマンス的に問題がありそうですが、シンプルなコードでスレッドセーフであることも考えると無難な設計かもしれません。

	public static synchronized Singleton getInstance() { // MonitorEnter
		// EnterLoadバリア
		if (instance == null) { // Normal Load (1)
			instance = new Singleton(); // Normal Store (2)
		}
		return instance; // Normal Load(3)
		// StoreExitバリア
	} // MonitorExit
対策3:volatileフィールドと固有ロック(synchronized)を併せて使う

次は、volatileとsynchronizedを使ったダブルチェッキングロジックの場合。
volatileを使ってロックの争奪を防ぐ仕組みを提供しますので、パフォーマンスを気にする場合は有効かもしれません。
Volatile Load (1) は 複数回呼ばれることを想定すると、Volatile Load (1)の前にLoadLoadバリアが発生すると考えられます。メインメモリからinstanceフィールドの最新の値を取得できます。
instanceがnullの場合はロックを獲得します。ロックを取得する前に他のスレッドがロックを解放した可能性があります。その結果を事前発生の仕組みで見るために、Volatile Load (3)で最新の値をロードします。すでに初期化済みであればロックを解放し、最新の値を返します。初期化済みでなければ、インスタンスを生成してinstanceフィールドにストアします。ストアの結果はメインメモリに反映されます。
ロック回避という点でメリットがあるが、コードが複雑というのがデメリットです。あと、そもそもJava5以前のJVMではこのコードは期待通りに動作しません。

	private static volatile Singleton instance;

	public static Singleton getInstance() {
		// LoadLoadバリア
		// LoadStoreバリア (*)
		Singleton result = instance; // Volatile Load (1)
		if (result == null) {
			// LoadEnterバリア
			synchronized (Singleton.class) { // MonitorEnter (2)
				// EnterLoadバリア
				// EnterStoreバリア
				result = instance; // Volatile Load (3)
				if (result == null) {
					// LoadStoreバリア
					// StoreStoreバリア (*)
					result = instance = new Singleton(); // Volatile Store (4)
					// StoreLoadバリア (*)
				}
				// LoadExitバリア
				// StoreExitバリア
			} // MonitorExit (5)
			// ExitEnterバリア
		}
		return result;
	}
対策4:内部クラスを使って遅延初期化する

最終奥義に近いのがこれ。遅延初期化ホルダーイデオムとか、オンデマンド初期化ホルダークラスイデオムとかいうらしい。
まず初期化の順番としては、スレッドが初めてgetInstance()を呼び出すと、InstanceHolder.instanceが参照されます。この時にnew Singleton()が実行され初期化されます。
根拠は「Java言語仕様3版」の「12.4.1 初期化が行われる時」 P281から引用。

Tによって宣言されているstaticフィールドが使用され,そのフィールドが定数変数(4.12.4)でない場合。

staticフィールドが定数でない場合は使用される時に初期化されるということです。
このフィールドはfinalフィールドなのでリオーダーが禁止され、初期化後にStoreStoreのメモリバリア命令が挿入されるので、メインメモリにその結果が反映されます。そしてスレッドから可視になります。初期化が完了すれば通常の変数として同期化不要で参照できます。
コードもシンプルで同期化も不要(つまり、ロックフリー)ということなので、これが一番よいかもしれません。

	private static class InstanceHolder{
        	private static final Singleton instance = new Singleton();
	}

	public static Singleton getInstance() {
		return InstanceHolder.instance;
	}

おまけ

もちろん、遅延初期化しないシングルトンもありですね。
Scalaのobjectも以下のようなコードだったと思う。

public class EagerSingleton {
	public static final EagerSingleton INSTANCE = new EagerSingleton();
// ...
}

The JSR-133 Cookbookには以下のようにあります。

Static final initialization requires StoreStore barriers that are normally entailed in mechanics needed to obey Java class loading and initialization rules.
static finalの初期化は StoreStoreバリアを要求する.通常これは, Javaのクラスローディングと初期化ルールに従うのに必要とされるメカニズムに含まれている.

StoreStoreバリアが発生するので初期化安全性があります。

// 以下のメソッドを複数スレッドで実行していると想定
public void func() {
    EagerSingleton instance = EagerSingleton.INSTANCE;
    // StoreStoreバリア -- 後続のストアより前にストアが行われる
    // ...
    instance.doProcess();
}

enum版も単純でよいですね。

public enum EnumSingleton {
	INSTANCE;
// ...
}

このコードをjadると以下のような感じになります。
INSTANCEフィールドは上記と同じstatic finalフィールドなのでStoreStoreバリアが発生しますので初期化安全性がありますね。
シングルトンというだけであれば、本質的には上記の通常のstatic final版の方がよいと思います。

public final class EnumSingleton extends Enum {
    public static final EnumSingleton INSTANCE;
    private static final EnumSingleton $VALUES[];

    static
    {
        INSTANCE = new EnumSingleton("INSTANCE", 0);
        $VALUES = (new EnumSingleton[] {
            INSTANCE
        });
    }

    public static EnumSingleton[] values()
    {
        return (EnumSingleton[])$VALUES.clone();
    }

    public static EnumSingleton valueOf(String name)
    {
        return (EnumSingleton)Enum.valueOf(test2/EnumSingleton, name);
    }

    private EnumSingleton(String s, int i)
    {
        super(s, i);
    }

}

これら二つは、finalフィールドなので同期化不要でスレッドセーフです。

まとめ

まとめというほどではないですが、遅延初期化するなら対策4のホルダーが好きかな、遅延初期化しないならやっぱりstatic finalフィールドが最強。まぁこういうのはチーム内で考えて標準化するとよいと思います。

*1:Volataile Load前にLoadStoreが挿入されるのは意味としては理解が難しいです。いずれにしてもvolatileフィールドの読み書きの前後にはバリア命令が挿入されるということが理解できればよいかと思います。

生成だけではなく複製もファクトリに任せたほうがよい

DDDで設計を始めると不変条件を維持するために、エンティティなどの可変オブジェクトの複製を行うことがよくあります。
Javaの場合は、Cloneableインターフェイスを実装して、実装型に応じた複製インスタンスを返すcloneメソッドを作る。以下のような感じ。

public class Employee implements Cloneable {

    private String name;
    // setter, getter 省略  

    @Override
    public Employee clone() {
        try{
            return (Employee)super.clone();
        } catch (CloneNotSupportedException e){
            throw new Error(e);
        }
    }

}

このcloneメソッドは便利なので普通に使っていたのですが、最近、簡単に使ってよいのだろうか、本来の責務としてずれていないかと思うようになってきました。ということで、今更ながらいろんな文献ひっくり返しました(^^ゞ
Joshua Blochが「Effective Java 第二版」「項目17 継承のために設計および文書化する、でなければ継承を禁止する」P88 で こういうこと言っています。

継承を可能にするためには、クラスが従わなければならない制約が多少あります。直接的、間接的のどちらであってもコンストラクタは、オーバーライド可能なメソッドを呼び出してはいけません。

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)

パズルでアンチパターンを学ぶ

この意味を理解するのにちょうど良いサンプルは、「JAVA PUZZLERS 罠、落とし穴、コーナーケース」の「第6章 洒落たパズラー」の「パズル51:何が言いたいの? (What's the Point?)」が参考になります。パズルのソースコードは以下。ColorPointのmainメソッドで何が表示されると思いますか?
[rakuten:book:11547432:detail]

// 座標を表すクラス
public class Point {
	private final String name; // 生成時にキャッシユされる
	protected final int x, y;
	// setter,getter省略

	public Point(int x, int y) {
		this.x = x;
		this.y = y;
		name = makeName();
	}

	protected String makeName() {
		return "[" + x + "," + y + "]";
	}

	@Override
	public final String toString() {
		return name;
	}
}
// 色を持つPointクラス
public class ColorPoint extends Point {

	public static void main(String[] args) {
		System.out.println(new ColorPoint(4, 2, "purple"));
	}

	private final String color;

	public ColorPoint(int x, int y, String color) {
		super(x, y);
		this.color = color;
	}

	@Override
	protected String makeName() {
		return super.makeName() + ":" + color;
	}
}

結果は以下です。

[4,2]:null

Pointクラスのコンストラクタでオーバーライド可能なmakeNameメソッドを呼び出しています。サブクラスであるColorPointクラスはそのmakeNameメソッドをオーバーライドしましたが、makeNameメソッドはthis.colorが初期化されるより前に呼ばれてしまいます。だから結果が"[4,2]:null"なのです。
パズルでは以下のように指摘しています。

型に対応したデフォルト値の状態であっても、final フィールドに値が代入される前に、そのフィールドの値を読み出すととが可能であることを、このパズルは示しています。

これは注意しなければなりません。

cloneメソッドはコンストラクタとかなり似た振る舞いをする

「Effective Java 第二版」「項目17 継承のために設計および文書化する、でなければ継承を禁止する」P88では、cloneメソッドはコンストラクタとかなり似た振る舞いとなると言っています。

継承のために設計されているクラスでCloneableあるいはSerializableを実装すると決めたならば、cloneメソッドとreadObjectメソッドはコンストラクタとかなり似た振る舞いをするので、同様の制限が適用されることを認識しなければなりません。その制限とは、cloneおよびreadObjectは、オーバーライド可能なメソッドを、直接的、間接的のどちらであっても呼び出してはいけないということです。

そしてパズルでもこれらのメソッドは擬似コンストラクタだと言っています。

(これらのメソッドは、コンストラクタを呼び出すととなくオブジェクトを生成するので、疑似コンストラクタと呼ばれます。)

なので、cloneメソッドでも、先ほどのオーバーライド可能なメソッドを呼び出すのは避けたほうがよいとのこと。

cloneメソッド内部でオーバーライドしたメソッドを呼んでみる

それでは、実際に車のオブジェクトで考えてみます。
車の抽象クラスであるAbstractCarクラスのcloneメソッドでは、super.clone()以外にtiresフィールドはマップです。つまり、同一インスタンスで状態変更可能な可変オブジェクトです。複製する時は新たなマップのインスタンスを作る必要があるので、cloneTiresメソッドを使って複製を作っています。そして、tiresフィールドのマップに格納する要素型もTireクラスも可変オブジェクトです。つまり、cloneTiresメソッドで複製する際は要素型のインスタンスの複製を作成し、それを保持する新たなマップのインスタンスを作る必要があります。

public abstract class AbstractCar implements Cloneable {
	private final String id;
	// タイアのマップ
	private Map<Position, Tire> tires = new HashMap<Position, Tire>();

	public AbstractCar(String id) {
		this.id = id;
	}

	// srcを複製するメソッド
	protected Map<Position, Tire> cloneTires(Map<Position, Tire> src) {
		Map<Position, Tire> result = new HashMap<Position, Tire>();
		Set<Entry<Position, Tire>> entrySet = src.entrySet();
		for (Entry<Position, Tire> entry : entrySet) {
			result.put(entry.getKey(), entry.getValue().clone());
		}
		return result;
	}

	// cloneメソッド
	@Override
	public AbstractCar clone() {
		try {
			AbstractCar result = (AbstractCar) super.clone();
			// tiresフィールドをディープコピー
			result.tires = cloneTires(tires);
			return result;
		} catch (CloneNotSupportedException e) {
			throw new Error(e);
		}
	}

	public final String getId() {
		return id;
	}

	public void addTire(Tire tire) {
		tires.put(tire.getLocalId(), tire);
	}

	public Set<Tire> getTires() {
		Set<Tire> result = new HashSet<Tire>();
		for (Tire t : tires.values()) {
			result.add(t.clone());
		}
		return result;
	}

	// hashCode, equalsメソッドは省略


}

AbstractCarクラスを継承するLegacyクラスでcloneTiresメソッドをオーバーライドして、ConcurrentHashMapの複製を作っている。かのようですが、要素型の複製を忘れているので不変条件が破壊されています。つまり、legacy.clone() してもなお要素のオブジェクトへの参照は共有していることになるわけです。もう一つの問題は、tiresフィールドは本来はfinalフィールドにしたいのですが、cloneメソッドのために再代入を許可しないといけないので、非finalフィールドとしています。

public class Legacy extends AbstractCar {

	public Car(String id, Engine engine) {
		super(id);
		this.engine = engine;
	}

	private Engine engine;

	// こんなオーバーライドをさせると危険。
	@Override
	protected Map<Position, Tire> cloneTires(Map<Position, Tire> src) {
		Map<Position, Tire> result = new ConcurrentHashMap<Position, Tire>();
		result.putAll(src); // 要素のcloneが抜けた!
		return result;
	}

	@Override
	public Car clone() {
		Car result = (Car) super.clone();
		result.engine = engine.clone();
		return result;
	}

	public void setEngine(Engine engine) {
		this.engine = engine.clone();
	}

	public Engine getEngine() {
		return engine.clone();
	}
}

こういう問題を回避するには、cloneTiresメソッドをfinalとしてそもそもオーバーライド不可能にするという方法です。

public abstract class AbstractCar implements Cloneable {
	// ...
	// finalメソッドにして、そもそもサブクラスでオーバーライドさせない
	protected final Map<Position, Tire> cloneTires(Map<Position, Tire> src) {
		Map<Position, Tire> result = new HashMap<Position, Tire>();
		Set<Entry<Position, Tire>> entrySet = tires.entrySet();
		for (Entry<Position, Tire> entry : entrySet) {
			result.put(entry.getKey(), entry.getValue().clone());
		}
		return result;
	}
	// ...
}

オーバーライド可能なメソッドを呼ばない、呼ばせない工夫はもちろんやるべきです。
しかし、そもそも ディープコピーを注意深く扱うことはモデルにとって重い責務ではないかと思い始めました。擬似コンストラクタとも呼ばれている所以がわかる気がします。DDDではオブジェクの生成にはファクトリメソッドやファクトリクラスを使うことを推奨しますが、複製もファクトリなのかもしれません。

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

cloneメソッドではなく「自前のコピーメソッド」を利用する

最近買った「Java ルールブック」の「3.1.5 Cloneable#clone()を使わず、目前のコピーメソッドを利用する」P156 を読んでなるほどと思いました。

Javaルールブック ?読みやすく効率的なコードの原則

Javaルールブック ?読みやすく効率的なコードの原則


cloneメソッドはシャローコピーなので、そこで可変オブジェクトなどを考慮してディープコピーすることを忘れたり、非finalによる初期化漏れの心配もあります。完全なオブジェクトを作るという意味からすると、finalをつけたいところです。そういう抜け漏れによって思わぬ不具合を発生させてしまうかもしれません。なので、別のコピーメソッドで複製インスタンスを作ったほうがよいという考え方です。*1

	@Override
	public AbstractCar clone() {
		try {
			AbstractCar result = (AbstractCar) super.clone();
			// ディープコピーをし忘れる可能性がある。
			// tiresフィールドが非finalによる代入漏れの心配もある。
			return result;
		} catch (CloneNotSupportedException e) {
			throw new Error(e);
		}
	}

この指針に従って「自前のコピーメソッド」を作ってみました。

public class Car {
	private final String id;
	private final Map<Position, Tire> tires = new HashMap<Position, Tire>();

	public Car(String id) {
		this.id = id;    
	}

	public static Car copy(Car car) {
		Car result = new Car(car.getId(), Engine.copy(car.getEngine()));
		Set<Entry<Position, Tire>> entrySet = car.tires.entrySet();
		for (Entry<Position, Tire> entry : entrySet) {
			result.tires.put(entry.getKey(), Tire.copy(entry.getValue()));
		}
		return result;
	}

}

当然、自クラスだけで完結するので単純にコードを書けますね。このパターンでは、tiresフィールドをfinalフィールド化することができるようになりました。これはスレッドセーフなモデルを考える上で都合がよいですね。

複製を「擬似生成」と考えるとファクトリの責務になるのではないか

複製を作るメソッドはコンストラクタと似た振る舞いをすると考えると、モデル側ではCloneableインターフェイスの実装とcloneメソッドは廃止して、ファクトリメソッドや、ファクトリクラスに複製処理を委譲したほうがよいかもしれません。
Legacyクラスであれば、以下のようなLegacyFactoryクラスを実装してはどうかという話しですね。つまり、「自前のコピーメソッド」というのを拡大解釈して、複製時の不変条件を守るということを考えると、DDDのファクトリパターンでオブジェクトの複製を実現した方がよいのではないかという視点です。

public final class LegacyFactory {

	private final EngineFactory engineFactory = new EngineFactory();
	private final TireFactory tireFactory = new TireFactory();

	// 通常の生成
	public Legacy newLegacy(String id, Engine engine) {
		return new Legacy(id, engine);
	}
	
	// 擬似生成(複製)を行う 既存のLegacyを元に同じ値を持つ新しいLegacyを作る
	public Legacy newLegacy(Legacy car) {
		Legacy legacy = new Legacy(car.getId(), engineFactory.newEngine(car
				.getEngine()));
		Set<Entry<Position, Tire>> entrySet = car.tires.entrySet();
		for (Entry<Position, Tire> entry : entrySet) {
			legacy.tires.put(entry.getKey(),
					tireFactory.newTire(entry.getValue()));
		}
		return legacy;
	}
}

AbstractCarのcloneメソッドの戻り値は、AbstractCarです。そして、そのサブクラスのLegacyのcloneメソッドの戻り値はLegacyです。つまり、cloneメソッド内部で自クラスのキャストがどうしても発生しますが、ファクトリを導入すればそういった問題はそもそも回避できます。

まとめ

まとめると以下ということになると思います。

  • 複製というのはそもそもモデルの責務ではなく、ファクトリのメソッドではないか。
  • スレッドセーフを考慮するならば、できる限りモデルのフィールドはfinalフィールドにしたほうがよい。

当然、ファクトリにすることによるデメリットもあると思います。
多分、cloneメソッドより抽象化が難しくなるでしょう。それと、実装型を知らない前提では、インスタンスの複製ができない。そういう場合はcloneメソッドしかないですね。
cloneの使いどころとしては、こちらも参照されたい。
Javaのcloneは悪者か? - 都元ダイスケ IT-PRESS
自分が作っているモデルでは、実装型を知らないということはないので、上記の二つを考えるとファクトリのほうが自然ではないかなと思いました。みなさんは、どう思いますか?

*1:「Effective Java第二版」の「項目11 cloneを注意してオーバーライドする」と関連する考え方だと思います。

ScalaのクラスをJavaの視点で解剖する

Scalaのクラスをちょっと解剖して、なぜそうなっているか読み取ってみようと思います。

ScalaのクラスをJavaのクラスに変換してみる

まず、こんなScalaのクラスを作ってみた。コンストラクタに氏と名のフィールドを二つ取る単純な人名クラスです。
class PersonName以降のカッコのところは、コンストラクタの引数を表しています。また、その引数そのものがフィールド定義となります。

package test

class PersonName(val firstName:String, val lastName:String)

これをscalacでクラスファイルにした後、jadデコンパイルしてJavaのソースファイルにしてみました。以下のJavaコードで読みとくと簡単に理解できると思います。*1

package test;

import scala.ScalaObject;

public class PersonName implements ScalaObject {
    private final String firstName; // val firtName:String
    private final String lastName; // val lastName:String

    public PersonName(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String firstName() {
        return firstName;
    }

    public String lastName() {
        return lastName;
    }

}

蛇足

getterに"get"が欲しければ以下のように書いてあげるともれなくgetterがプレゼントされます。

class PersonName(@BeanProperty val firstName:String, @BeanProperty val lastName:String)

まぁ、余分に作られますが、無いよりマシかもしれません。

    public String firstName() {
        return firstName;
    }

    public String getFirstName() {
        return firstName();
    }

    // lastName, getLastNameも同様に作られる。

valフィールド=finalフィールド

話を元に戻しますが、Scalaでは変数宣言は原則的にvalで記述します。今回のクラスもvalでフィールド宣言しています。
Scalaのクラスのデコンパイルした結果を見るとわかりますが、valフィールドがfinalフィールドになっています。
これまでのメモリモデルの話でも説明したように、finalフィールドはコンストラクト後に完全に初期化され、スレッドから見えることが保証されています。また、初期化後は同期化が不要でスレッドセーフです。(メモリバリアとしては、finalフィールドのストア後にStoreLoadバリアを発生させます。)

そういう意味では、並行処理でマルチコアのパワーをなるべく引き出さなければならないプログラムでは、finalフィールドは使って不変オブジェクトを作ることが多いので、"finalのつけ忘れのない"valの文化は必要だというのはよくわかりますね。

*1:ScalaObjectクラスはScalaのクラスに実装されるインターフェイスですかね。よくしらない。