戻り値 - t.class java




ジェネリック型Tのクラスインスタンスを取得する方法 (12)

私はジェネリッククラス、 Foo<T>を持っています。 Fooメソッドでは、T型のクラスインスタンスを取得したいですが、 T.class呼び出すことはできません。

T.classを使ってそれをT.classするための好ましい方法は何ですか?


ここには実用的な解決策があります:

@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
    try {
        String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
        Class<?> clazz = Class.forName(className);
        return (Class<T>) clazz;
    } catch (Exception e) {
        throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
    }
} 

注意: スーパークラスとしてのみ使用できます。

  1. 型指定されたクラスで拡張する必要があります( Child extends Generic<Integer>

または

  1. 匿名の実装として作成する必要があります( new Generic<Integer>() {};

しかし、あなたが抽象としてあなたのFooクラスを定義する場合、小さな抜け穴があります。 つまり、クラスをインスタンス化する必要があります:

Foo<MyType> myFoo = new Foo<MyType>(){};

(最後に二重括弧があることに注意してください。)

これで、実行時にTの型を取得できます:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];

ただし、 mySuperclassT最終型を実際に定義するクラス定義のスーパークラスでなければなりません。

あまりエレガントではありませんが、 new Foo<MyType>(){}またはnew Foo<MyType>(MyType.class);を好むかどうかを判断する必要がありますnew Foo<MyType>(MyType.class); あなたのコードで。

例えば:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;

/**
 * Captures and silently ignores stack exceptions upon popping.
 */
public abstract class SilentStack<E> extends ArrayDeque<E> {
  public E pop() {
    try {
      return super.pop();
    }
    catch( NoSuchElementException nsee ) {
      return create();
    }
  }

  public E create() {
    try {
      Type sooper = getClass().getGenericSuperclass();
      Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];

      return (E)(Class.forName( t.toString() ).newInstance());
    }
    catch( Exception e ) {
      return null;
    }
  }
}

次に:

public class Main {
    // Note the braces...
    private Deque<String> stack = new SilentStack<String>(){};

    public static void main( String args[] ) {
      // Returns a new instance of String.
      String s = stack.pop();
      System.out.printf( "s = '%s'\n", s );
    }
}

ジェネリックスを使用しているクラス/インタフェースを拡張または実装している場合は、既存のクラス/インタフェースをまったく変更することなく、親クラス/インタフェースの汎用タイプを取得できます。

3つの可能性がありますが、

ケース1クラスがGenericsを使用しているクラスを拡張しているとき

public class TestGenerics {
    public static void main(String[] args) {
        Type type = TestMySuperGenericType.class.getGenericSuperclass();
        Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
        for(Type gType : gTypes){
            System.out.println("Generic type:"+gType.toString());
        }
    }
}

class GenericClass<T> {
    public void print(T obj){};
}

class TestMySuperGenericType extends GenericClass<Integer> {
}

ケース2クラスがGenericsを使用しているインターフェイスを実装しているとき

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

class TestMySuperGenericType implements GenericClass<Integer> {
    public void print(Integer obj){}
}

ケース3インターフェイスがGenericsを使用しているインターフェイスを拡張している場合

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

interface TestMySuperGenericType extends GenericClass<Integer> {
}

一般的な抽象スーパークラスがあるとします。

public abstract class Foo<? extends T> {}

そして、Tを拡張する一般的なBarを使ってFooを拡張する2番目のクラスがあります:

public class Second extends Foo<Bar> {}

FooクラスのBar.classクラスを取得するには、 Type (bert bruynoogheの回答から)を選択し、 Classインスタンスを使用してそれを推測します。

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);

この操作は理想的ではないことに注意する必要があります。したがって、この計算を避けるために、計算された値をキャッシュすることをお勧めします。 典型的な用途の1つは、ジェネリックDAOの実装です。

最終的な実装:

public abstract class Foo<T> {

    private Class<T> inferedClass;

    public Class<T> getGenericClass(){
        if(inferedClass == null){
            Type mySuperclass = getClass().getGenericSuperclass();
            Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
            String className = tType.toString().split(" ")[1];
            inferedClass = Class.forName(className);
        }
        return inferedClass;
    }
}

他の関数のFooクラスまたはBarクラスから呼び出されたときに返される値はBar.classです。


他の答えで説明したように、このParameterizedTypeアプローチを使用するには、クラスを拡張する必要がありますが、それはそれを拡張する全く新しいクラスを作るための余分な作業のようです...

したがって、抽象クラスを抽象クラスにすると、クラスを拡張しなければならないため、サブクラス化の要件を満たします。 (ロンボクの@Getterを使用)。

@Getter
public abstract class ConfigurationDefinition<T> {

    private Class<T> type;
    ...

    public ConfigurationDefinition(...) {
        this.type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        ...
    }
}

新しいクラスを定義せずにこれを拡張しましょう。 (最後の{}は拡張されていますが、あなたが望む場合を除いては何も上書きしません)。

private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
...
Class stringType = myConfigA.getType();
Class fileType = myConfigB.getType();


抽象的なジェネリッククラスでこの問題が発生しました。 この特定のケースでは、解決策は簡単です。

abstract class Foo<T> {
    abstract Class<T> getTClass();
    //...
}

後で派生クラスを作成します。

class Bar extends Foo<Whatever> {
    @Override
    Class<T> getTClass() {
        return Whatever.class;
    }
}

標準のアプローチ/回避策/解決策は、次のようにclassオブジェクトをコンストラクタに追加することです。

 public class Foo<T> {

    private Class<T> type;
    public Foo(Class<T> type) {
      this.type = type;
    }

    public Class<T> getType() {
      return type;
    }

    public T newInstance() {
      return type.newInstance();
    }
 }

私はこれを行うための一般的で簡単な方法を見つけました。 私のクラスでは、クラス定義内の位置に応じてジェネリック型を返すメソッドを作成しました。 次のようなクラス定義を仮定しましょう:

public class MyClass<A, B, C> {

}

次に、型を永続化するための属性をいくつか作成しましょう。

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;

// Getters and setters (not necessary if you are going to use them internally)

    } 

次に、汎用定義の索引に基づいて型を戻す汎用メソッドを作成できます。

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {
        // To make it use generics without supplying the class type
        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }

最後に、コンストラクタでメソッドを呼び出し、各タイプのインデックスを送信するだけです。 完全なコードは次のようになります。

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;


    public MyClass() {
      this.aType = (Class<A>) getGenericClassType(0);
      this.bType = (Class<B>) getGenericClassType(1);
      this.cType = (Class<C>) getGenericClassType(2);
    }

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {

        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }
}

私はクラスパスに余分な依存関係を追加せずにこれを自分で行う方法を探していました。 いくつかの調査の後、私あなたが一般的なスーパータイプを持っている限り可能であることを発見しました。 これは、一般的な層のスーパータイプを持つDAOレイヤーで作業していたので、私にとっては問題ありませんでした。 これがあなたのシナリオに合っていれば、それは一番きれいな方法です。

私が遭遇したほとんどのジェネリックのユースケースには、 ArrayList<T> GenericDAO<T>DAO<T> GenericDAO<T>などの汎用スーパータイプがあります。

純粋なJavaソリューション

Javaで実行時にジェネリック型にアクセスする記事では、純粋なJavaを使用して行うことができる方法について説明しています。

スプリングソリューション

私のプロジェクトでは、 Springを使用していました。これは、Springが型を見つけるための便利なユーティリティメソッドを持っているので、さらに優れています。 これは私にとって最善のアプローチです。 私は、あなたが独自のユーティリティメソッドを書くことができる、あなたが春を使用していない場合は推測します。

import org.springframework.core.GenericTypeResolver;

public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{

    @Autowired
    private SessionFactory sessionFactory;

    private final Class<T> genericType;

    private final String RECORD_COUNT_HQL;
    private final String FIND_ALL_HQL;

    @SuppressWarnings("unchecked")
    public AbstractHibernateDao()
    {
        this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
        this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
        this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
    }

簡単な答えは、Javaのジェネリック・タイプ・パラメーターのランタイム・タイプを見つける方法がないことです。 詳細は、 Javaチュートリアルの型消去に関する章を読むことをお勧めします。

これに対する一般的な解決策は、型パラメータのClassをジェネリック型のコンストラクタに渡すことです。

class Foo<T> {
    final Class<T> typeParameterClass;

    public Foo(Class<T> typeParameterClass) {
        this.typeParameterClass = typeParameterClass;
    }

    public void bar() {
        // you can access the typeParameterClass here and do whatever you like
    }
}

   public <T> T yourMethodSignature(Class<T> type) {

        // get some object and check the type match the given type
        Object result = ...            

        if (type.isAssignableFrom(result.getClass())) {
            return (T)result;
        } else {
            // handle the error
        }
   }




generics