委譲
次は委譲のコードを見てみましょう。委譲とは、別のクラスに機能を任せる際に使われます。継承と似ていますが、オブジェクト指向のis-aの関係になく、単に同じ機能を実装したい場合にはこの委譲が使われれます。Javaの場合は単一継承のみが許されていますので、委譲を使いたい場面がよくあります。
下記では、CardListというクラスが内包したlistインスタンスに機能を委譲しています。
public class CardList {
List<String> list = new ArrayList<String>();
public boolean add(final String arg0) {
return list.add(arg0);
}
public boolean remove(final Object arg0) {
return list.remove(arg0);
}
}
@Delegate
Lombokでは、@Delegateを利用して委譲を実装できます。
public class CardList {
@Delegate
List<String> list = new ArrayList<String>();
}
@Delegateアノテーションを適用すると、そのフィールドの型に定義された公開メソッドと同じメソッドがフィールドを持つクラスに定義され、そのメソッドが呼び出されると、@Delegateが指定されたインスタンスに処理を委譲します。
同期化
複数スレッドを使ってプログラムを実行する場合には、必要に応じて処理の同期が必要です。最も単純な同期化はメソッドにsynchronizedを付与することです。
public class Counter {
public synchronized void countup() {
// 同期化された処理
}
}
メソッドにsynchronizedを付与するのは下記のコードと同義で、自分自身のインスタンスをロックオブジェクトに指定しています。
このsynchronizedを付与したメソッドは、常に1つのスレッドでのみ実行されると誤解されがちですが、thisをロックオブジェクトとしてsynchronized宣言しているため、インスタンスが異なれば複数のスレッドから同時に実行されます。
public class Counter {
public void countup() {
synchronized (this) {
// 同期化された処理
}
}
}
ロックオブジェクトにthisを利用する場合、このインスタンスが参照可能な処理でロックオブジェクトに指定することが可能なため、他の処理でロックオブジェクトに指定するなどして、意図しないロック待ちやデッドロックなどが発生してしまう可能性があります。
下記のように明示的にロックオブジェクトを実装してしまえば、そのような混乱をさけることが可能です。
public class Counter {
private final Object lock = new Object();
public void countup() {
synchronized(lock) {
// 同期化された処理
}
}
}
@Synchronized
Lombokでは、メソッドに付与できる@Synchronizedアノテーションが用意されています。このアノテーションで、上記のように明示的にロックオブジェクトを利用したsynchronizedと同等のことが実現できます。
public class Counter {
@Synchronized
public void countup() {
// 同期化された処理
}
}
| 属性 | 説明 |
|---|---|
| value | ロックオブジェクト名を明示的に指定します。 |
まとめ
Lombokを使う際の注意点は、Javaの言語仕様とは違った形で実装するため、この仕様に慣れ親しんだプログラマに対しては、一見違和感を与えてしまうことです。
この点に注意すれば、Javaを使ってより可読性の高いコーディングが可能ですので、使ってみてはいかがでしょうか。
