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プロジェクトなので、インポートするのは簡単なはずです。そのまま実行します。