1. 概要

この記事では、 Guava reflection APIについて説明します。これは、標準のJavaReflectionAPIと比較して間違いなく汎用性があります。

Guava を使用して実行時にジェネリック型をキャプチャし、Invokableも活用します。

2. 実行時の汎用タイプのキャプチャ

Javaでは、ジェネリックは型消去で実装されています。つまり、ジェネリック型情報はコンパイル時にのみ利用可能であり、実行時には利用できなくなります。

例えば、 リスト ジェネリック型に関する情報は実行時に消去 。 そのため、実行時に汎用Classオブジェクトを渡すことは安全ではありません。

ジェネリックタイプが異なる2つのリストを同じ参照に割り当てることになる可能性がありますが、これは明らかに良い考えではありません。

List<String> stringList = Lists.newArrayList();
List<Integer> intList = Lists.newArrayList();

boolean result = stringList.getClass()
  .isAssignableFrom(intList.getClass());

assertTrue(result);

型消去のため、メソッド isAssignableFrom()はリストの実際の汎用型を知ることができません。 基本的に、実際のタイプに関する情報がないリストである2つのタイプを比較します。

標準のJavaリフレクションAPIを使用することで、一般的なタイプのメソッドとクラスを検出できます。 を返すメソッドがある場合リスト 、リフレクションを使用して、そのメソッドの戻り型を取得できます– a 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));

整数クラスは番号クラスを拡張するため、integerListTokenのみをタイプnubmerTypeTokenの参照に割り当てることができます。[X169X ]

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));

の実際の返品タイプを取得します関数 、 あれは弦。 マップ内のエントリのタイプを取得することもできます。

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 は、java.lang.reflect.Methodおよびjava.lang.reflect.Constructorの流暢なラッパーです。 標準のJavareflectionAPIに加えてよりシンプルなAPIを提供します。 2つのパブリックメソッドを持つクラスがあり、そのうちの1つがfinalであるとしましょう。

class CustomClass {
    public void somePublicMethod() {}

    public final void notOverridablePublicMethod() {}
}

次に、GuavaAPIと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);

このような単純な操作でも、標準のreflectionAPIを使用して多くのチェックが必要であることがわかります。 Invokable クラスは、これをAPIの背後に隠し、使いやすく、非常に簡潔です。

5. 結論

この記事では、GuavaリフレクションAPIを調べて、標準のJavaと比較しました。 実行時にジェネリック型をキャプチャする方法と、 Invokableクラスがリフレクションを使用するコードにエレガントで使いやすいAPIを提供する方法を見てきました。

これらすべての例とコードスニペットの実装は、 GitHubプロジェクトにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。