【解決方法】Javaでジェネリック型名を取得する


Java で型の名前を取得するには、 getTypeName 型のクラスに対する関数。 たとえば、次のコード行は次のようになります。

Java
System.out.println(String.class.getTypeName());

印刷されます:

java.lang.String

ただし、ジェネリック型を扱う場合、次のコード行はコンパイルされません ( T はジェネリック型です):

Java
T.class.getTypeName()

ジェネリック型のクラスを取得する (そしてそこから型の名前を取得する) 方法はありますか?

たとえば、次のクラスがあるとします。

Java
package tries1;

public class Person {
    // ...
}

そして次の汎用クラス:

Java
class MyGenericClass<T> {
    public String getParameterTypeName() {
        // Here is the place where we should get the generic type (T) name.
    }
}

次のコード:

Java
MyGenericClass<Person> g = new MyGenericClass<>();
System.out.println(g.getParameterTypeName());

印刷する必要があります:

tries1.Person

ジェネリック型のクラス (取得したいもの) もジェネリック型のインスタンスもありません (したがって、呼び出すことができません) getClass インスタンス上)。

何か案が?

私が試したこと:

インターネットで解決策を検索しましたが、役立つものが見つかりませんでした。

こちらも使ってみました ParameterizedType ここで説明されているように: https://farizfadian.blogspot.com/2020/02/get-class-name-from-generic-java-class.html[^]:

Java
class MyGenericClass<T> {
    public String getParameterTypeName() {
        return ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).
                getActualTypeArguments()[0]).getTypeName();
    }
}

しかし、次のような例外が発生しました。class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')」。

次のように派生クラスから呼び出したとき:

Java
class MyGenericClass<T> {
    class MyDerivedFromGeneric extends MyGenericClass<T> {
        public String getParameterTypeName() {
            return getParameterTypeNameImpl();
        }
    }

    public String getParameterTypeName() {
        MyDerivedFromGeneric dg = new MyDerivedFromGeneric();
        return dg.getParameterTypeName();
    }
    
    protected String getParameterTypeNameImpl() {
        return ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).
                getActualTypeArguments()[0]).getTypeName();
    }
}

次のような例外が発生しました:class sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to class java.lang.Class (sun.reflect.generics.reflectiveObjects.TypeVariableImpl and java.lang.Class are in module java.base of loader 'bootstrap')」。

キャストを外したところ、 Class<T>:

Java
protected String getParameterTypeNameImpl() {
    return ((ParameterizedType) getClass().getGenericSuperclass()).
        getActualTypeArguments()[0].getTypeName();
}

印刷されます:

T

(ない tries1.Person 予想通り。)

私が試したもう 1 つの方向は、定義されたフィールドでリフレクションを使用することです。

Java
class MyGenericClass<T> {
    public T t1;

    public String getParameterTypeName() {
        try {
            var thisClass = getClass();
            var t1Field = thisClass.getField("t1");
            var t1Type = t1Field.getType();
            return t1Type.getTypeName();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

ただし、次のように出力されます。

java.lang.Object

(繰り返しますが、そうではありません tries1.Person 予想通り。)

解決策 2

により タイプ消去、コンパイラはジェネリック型 (T) の最初の境界 (Object 私たちの場合には)。 したがって、定義された汎用フィールドの型を取得しようとすると、次の結果が得られました。 Object (そうではない Person 私の期待通りに)。

したがって、ジェネリック宣言だけからジェネリックパラメータの実際の型を取得することは不可能のようです。 ジェネリック パラメーターの実際の型が必要な場合は、別の方法でそれをクラスに注入する必要があります (例: コンストラクター パラメーター、実際の型を返す抽象関数など)。

解決策 1

これらの呼び出しは、生成されたクラスの実際のオブジェクトに対してのみ機能することがわかると思います。 コンパイル時に、 T prefix には値はなく、コンパイラの単なるマーカーです。 見る クラス (Java プラットフォーム SE 8)[^] 利用可能なオプションについては。

コメント

タイトルとURLをコピーしました