ぺんぎんらぼ

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です。たまに絵を描きます。

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です

Java Tips - メソッドの復帰値の型を総称型も含めて取得する

f:id:penguinlabo:20210908000148j:plain

Javaでプログラム内からメソッドの情報を取得する場合、java.lang.reflect.Methodクラスを使用して取得することができます。

今回、以下のメソッドの復帰値の型を取得します。

public List<String> getStringList() {
    ・・・
}

メソッドの復帰値の型を取得

java.lang.reflect.MethodクラスのgetReturnTypeメソッドにより、メソッドの復帰値の型を取得することができます。

Class returnType = method.getReturnType();
System.out.println("returnType = " + returnType.getName());

これにより、メソッドの復帰値の型がClassオブジェクトとして取得できます。
このコードの実行結果は以下のようになります。

returnType = java.util.List

メソッドの復帰値の型であるjava.util.Listクラスのクラスオブジェクトが取得できていることがわかります。

メソッドの復帰値の総称型を取得

java.lang.reflect.MethodクラスのgetReturnTypeメソッドでは、メソッドの復帰値の「型」の部分を取得できますが、総称型が取得できません。

総称型の情報を含めてメソッドの復帰値の型を取得する場合、Java5で追加されたjava.lang.reflect.MethodクラスのgetGenericReturnTypeメソッドを使用します。

Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
    ParameterizedType paramType = (ParameterizedType) returnType;
    Type[] argTypes = paramType.getActualTypeArguments();
    for (int i = 0; i < argTypes.length; i++) {
        System.out.println("returnType argument[" + i + "] = " + argTypes[i]);
    }
}

getGenericReturnTypeメソッドは、復帰値の型の情報だけを取得するのではなく、復帰値の型を総称型の情報を含めて取得するものです。

getGenericReturnTypeメソッドの復帰値はTypeインターフェースです。実際の復帰値はメソッドの復帰値の型によって変わります。
メソッドの復帰値の型が総称型を持つ場合、getGenericReturnTypeメソッドの復帰値はParameterizedTypeインターフェースとなります。このParameterizedTypeインターフェースのメソッドを使用して総称型を取得することになります。

総称型の取得方法

ParameterizedTypeインターフェースのgetActualTypeArgumentsメソッドを使用して、総称型を取得します。

    ParameterizedType paramType = (ParameterizedType) returnType;
    Type[] argTypes = paramType.getActualTypeArguments();

総称型は元クラスによって複数存在する場合があるので、getActualTypeArgumentsメソッドの復帰値は、Typeインターフェースの配列です。
例えば、メソッドの復帰値がListインターフェースの場合、総称型は1つですが、Mapインターフェースの場合、総称型は2つになります。
また、総称型が入れ子になる場合があるので、getActualTypeArgumentsメソッドの復帰値はClassの配列ではなく、Typeインターフェースの配列になっています。

総称型の判定方法

getActualTypeArgumentsメソッドの復帰値は、Typeインターフェースの配列のため、総称型に特定のクラスが指定されていることを判定する場合、一工夫必要です。

以下のコードで、総称型がStringクラスであることを判定できます。

Type[] argTypes = paramType.getActualTypeArguments();
for (int i = 0; i < argTypes.length; i++) {
    if (argTypes[i] instanceof Class && ((Class) argTypes[i]) == String.class) {
        // 総称型がStringクラス
        ・・・
    }
}

総称型がStringクラスの場合、getActualTypeArgumentsメソッドの復帰値はTypeインターフェースを実装したClassクラスになります。
なので、getActualTypeArgumentsメソッドの復帰値がClassクラスであることを判定した後、Classクラスにキャストして、そのClassオブジェクトがStringクラスであることを判定します。

今回のポイント

  1. メソッドの復帰値を総称型を含めて取得する場合、java.lang.reflect.MethodクラスのgetGenericReturnTypeメソッドを使用する。
  2. java.lang.reflect.MethodクラスのgetGenericReturnTypeメソッドの復帰値はTypeインターフェース。
  3. 復帰値のTypeインターフェースは、型の内容によって、Typeインターフェースを継承or実装した、いくつかのクラスに分岐する。

java.lang.reflect.MethodクラスのgetGenericReturnTypeメソッドは詳細な情報が取得できる半面、取得方法が複雑になります。
取得した情報のクラスが取得する内容によって変化することを気にする必要があります。

f:id:penguinlabo:20210907123730p:plain