グアバのリフレクションユーティリティの手引き
1概要
この記事では、
Guava
reflection
APIについて見ていきます。これは、標準のJavaリフレクションAPIと比較して明らかに用途が広いです。
実行時には
Guava
を使用してジェネリック型をキャプチャします。また、
httpsを有効に活用します。 .html[Invokable]
も同様です。
2実行時にジェネリック型を取得する
-
Javaでは、ジェネリックは型消去で実装されています** つまり、ジェネリック型情報はコンパイル時にしか利用できず、実行時にしか利用できなくなります – 利用できなくなります。
たとえば、
List <String>、
ジェネリック型に関する情報はhttps://docs.oracle.com/javase/tutorial/java/generics/erasure.html[実行時に消去]を取得します。そのため、実行時に汎用の
Class
オブジェクトを渡すことは安全ではありません。
異なる総称型を持つ2つのリストを同じ参照に割り当てることになるかもしれませんが、これは明らかにお勧めできません。
List<String> stringList = Lists.newArrayList();
List<Integer> intList = Lists.newArrayList();
boolean result = stringList.getClass()
.isAssignableFrom(intList.getClass());
assertTrue(result);
消去タイプのため、メソッド
isAssignableFrom()
はリストの実際の総称タイプを知ることができません。それは基本的に実際の型についての情報なしで
List
である2つの型を比較します。
標準のJavaリフレクションAPIを使用することで、メソッドやクラスのジェネリック型を検出できます。
List <String>
を返すメソッドがある場合は、リフレクションを使用してそのメソッドの戻り型を取得できます。https://docs.oracle.com/javase/7/docs/api/java/lang/
List <String>
を表すreflect/ParameterizedType.html[
ParameterizedType
]。
TypeToken
クラスはこの回避策を使用してジェネリック型を操作できるようにします。
TypeToken
クラスを使用して、ジェネリックリストの実際の型を取得し、それらが実際に同じ参照によって参照できるかどうかを確認できます。
TypeToken<List<String>> stringListToken
= new TypeToken<List<String>>() {};
TypeToken<List<Integer>> integerListToken
= new TypeToken<List<Integer>>() {};
TypeToken<List<? extends Number>> numberTypeToken
= new TypeToken<List<? extends Number>>() {};
assertFalse(stringListToken.isSubtypeOf(integerListToken));
assertFalse(numberTypeToken.isSubtypeOf(integerListToken));
assertTrue(integerListToken.isSubtypeOf(numberTypeToken));
Integer
クラスは
Number
クラスを継承するので、
integerListToken
のみを
nubmerTypeToken
型の参照に割り当てることができます。
3
TypeToken
を使用した複合型の取得
一般的なパラメータ化クラスを作成し、実行時に一般的な型に関する情報を取得したいとします。その情報を取得するためのフィールドとして
TypeToken
を持つクラスを作成できます。
abstract class ParametrizedClass<T> {
TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
その後、そのクラスのインスタンスを作成するときに、ジェネリック型は実行時に利用可能になります。
ParametrizedClass<String> parametrizedClass = new ParametrizedClass<String>() {};
assertEquals(parametrizedClass.type, TypeToken.of(String.class));
また、複数のジェネリック型を持つ複合型の
TypeToken
を作成し、実行時にそれらの各型に関する情報を取得することもできます。
TypeToken<Function<Integer, String>> funToken
= new TypeToken<Function<Integer, String>>() {};
TypeToken<?> funResultToken = funToken
.resolveType(Function.class.getTypeParameters()[1]);
assertEquals(funResultToken, TypeToken.of(String.class));
Function
の実際の戻り型、つまり__Stringを取得します。マップ内のエントリの型を取得することもできます。
TypeToken<Map<String, Integer>> mapToken
= new TypeToken<Map<String, Integer>>() {};
TypeToken<?> entrySetToken = mapToken
.resolveType(Map.class.getMethod("entrySet")
.getGenericReturnType());
assertEquals(
entrySetToken,
new TypeToken<Set<Map.Entry<String, Integer>>>() {});
ここでは、Java標準ライブラリのリフレクションメソッド
getMethod()
を使用して、メソッドの戻り値の型を取得します。
4召喚状__
Invokable
はhttps://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html[
java.lang.reflect.Method
]およびhttps://docsの流暢なラッパーです。 oracle.com/javase/8/docs/api/java/lang/reflect/Constructor.html[
java.lang.reflect.Constructor
]。
これは標準のJavaの
reflection
APIの上に単純なAPIを提供します。
2つのパブリックメソッドを持つクラスがあり、そのうちの1つが最終クラスであるとしましょう。
class CustomClass {
public void somePublicMethod() {}
public final void notOverridablePublicMethod() {}
}
それでは、Guava APIとJava標準の
reflection
APIを使用して
somePublicMethod()
を調べてみましょう。
Method method = CustomClass.class.getMethod("somePublicMethod");
Invokable<CustomClass, ?> invokable
= new TypeToken<CustomClass>() {}
.method(method);
boolean isPublicStandradJava = Modifier.isPublic(method.getModifiers());
boolean isPublicGuava = invokable.isPublic();
assertTrue(isPublicStandradJava);
assertTrue(isPublicGuava);
これら2つのバリアントの間に大きな違いはありませんが、メソッドがオーバーライド可能かどうかを確認することは、Javaでは本当に簡単な作業です。
幸い、
Invokable
クラスの
isOverridable()
メソッドを使うと簡単になります。
Method method = CustomClass.class.getMethod("notOverridablePublicMethod");
Invokable<CustomClass, ?> invokable
= new TypeToken<CustomClass>() {}.method(method);
boolean isOverridableStandardJava = (!(Modifier.isFinal(method.getModifiers())
|| Modifier.isPrivate(method.getModifiers())
|| Modifier.isStatic(method.getModifiers())
|| Modifier.isFinal(method.getDeclaringClass().getModifiers())));
boolean isOverridableFinalGauava = invokable.isOverridable();
assertFalse(isOverridableStandardJava);
assertFalse(isOverridableFinalGauava);
このような単純な操作でも、標準の反射APIを使用して多くのチェックが必要になることがわかります。
Invokable
クラスは、これを使用するのが簡単で非常に簡潔なAPIの背後に隠しています。
5結論
この記事では、GuavaリフレクションAPIを見て、それを標準のJavaと比較しました。実行時にジェネリック型をキャプチャする方法と、リフレクションを使用しているコードに対して
Invokable
クラスがエレガントで使いやすいAPIを提供する方法を見ました。
これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/guava[GitHubプロジェクト]にあります – これはMavenプロジェクトなので、インポートするのは簡単なはずです。そのまま実行します。