Qiita で初 Java です。
Unit テストを書いていると、Java なんかではどうしても private なコンストラクタを実行したいケースというのが出てくるのですが、ググってみた限り、どうも使いやすいユーティリティなどがない様子。
private な field にセットしたり、private な setter とかをいい感じに扱ってくれる BeanUtils 的なのとか、そういうのはいくつでも見つかるものの、何故かコンストラクタは見つからないのです。
(あるよ!っていう情報あったら教えて下さい。枯れてる方がいいに決まってるので。)
そりゃリフレクション使えば書けるのは分かりきっているけど、こんな定型文みたいなの毎回書きたくないよね?…というわけでユーティリティクラス化したので置いておきます。
プリミティブ型の引数を取る場合でも、引数が複数の場合でも、比較的楽に書けるのがいいかなー、と思ってます。
こんな感じに使えます。
MyClass obj1 = ConstructorTestUtil.builder(MyClass.class).build();
MyClass obj2 = ConstructorTestUtil.builder(MyClass.class)
.types(boolean.class)
.args(true)
.build();
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
MyClass obj3 = ConstructorTestUtil.builder(MyClass.class)
.types(boolean.class, String.class, Map.class)
.args(false, "text", map)
.build();
MyClass.java
class MyClass {
private MyClass() {
System.out.println("no args.");
}
private MyClass(boolean arg1) {
System.out.println("primitive arg: " + arg1);
}
private MyClass(boolean arg1, String arg2, Map<Integer, String> arg3) {
this(arg1);
System.out.println("string arg: " + arg2);
System.out.println("map arg: " + arg3);
}
}
ConstructorTestUtil.java
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* This class can execute a private constructor.
* NOTE: Strongly recommended to use test only.
* MyPrivateClass obj = ConstructorTestUtils.builder(MyPrivateClass.class).types(Arg1.class, Arg2.class).args(arg1, arg2).build();
*/
public final class ConstructorTestUtil {
private ConstructorTestUtil() {}
public static <T> Builder<T> builder(Class<T> type) {
return new Builder<T>(type);
}
public static class Builder<T> {
private Class<T> type;
private Class<?>[] types;
private Object[] args;
private Builder(Class<T> type){
this.type = type;
};
public Builder<T> types(Class<?>... types){
this.types = types;
return this;
}
public Builder<T> args(Object... args){
this.args = args;
return this;
}
public T build() {
try {
Constructor<T> constructor = this.type.getDeclaredConstructor(this.types);
constructor.setAccessible(true);
return constructor.newInstance(this.args);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
まあ書いたはいいものの、結局使わなかったからいまここに公開しているっていう話だったりはするんですけどね。いつまた使いたくなるか分からないのでメモとして残しておきます。