Genericsのワイルドカードを使ったコピー・ファクトリ・メソッド
JAVALOBBYに、「Java Generics Wildcard Capture - A Useful Thing to Know」という記事が掲載されています。コピー・ファクトリ・メソッドやコピー・コンストラクタを作るときに、Genericsのワイルドカードを使うテクニックを紹介したものです。コピー・ファクトリ・メソッドというのは、既存のオブジェクトのパラメータをコピーした新しいオブジェクトを生成するメソッドということのようです。
コードを簡略化して書くと、
public class Field<T> { private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } }
こういうクラスがあったときに、
public class FieldUtil { public static Field<?> copy(Field<?> field) { Field<?> objField = new Field<?>(); objField.setValue(field.getValue()); return objField; } }
こういうメソッドを作りたいけど、このままじゃコンパイルできないから、どうしたらいいだろう、ということです。解決策としてはヘルパー・メソッドを使う方法が紹介されているのですが、それはひとまず置いておいて、なぜこれがダメなのかを考えてみます。
肝となっているのは
objField.setValue(field.getValue());
この部分です。これが返すエラーが「Field
copy()メソッドの引数であるfieldの型はワイルドカードを持っています。この場合、コンパイラはfieldがField
一方でfield.getValue()の方ですが、コンパイラはこの戻り値の?を未知のTという型であると推測します。?は「? extends Object」のことなので、コンパイラはfield.getValue()の戻り値はObjectであると判断します。つまり「capture#3-of ?」ではないし、「capture#3-of ?」にObjectが適用できるかも検証できないことから、前述のエラーメッセージになるというわけです。
このエラーの回避方法として紹介されているのが、次のようなヘルパー・メソッドを用意することです。これはGenericsのワイルドカード使用における「キャプチャー・ヘルパー」として知られるイディオムです。
public class FieldUtil { public static Field<?> copy(Field<?> field) { return copyHelper(field); } private static <T> Field<T> copyHelper(Field<T> field) { Field<T> objField = new Field<T>(); objField.setValue(field.getValue()); return objField; } }
copyHelper()はジェネリック・メソッドなので、その引数であるField