ジェネリッククラスがありFoo<T>
ます。のメソッドでFoo
、型のクラスインスタンスを取得したいのですが、T
呼び出すことができませんT.class
。
それを使用して回避するための好ましい方法は何T.class
ですか?
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
ジェネリッククラスがありFoo<T>
ます。のメソッドでFoo
、型のクラスインスタンスを取得したいのですが、T
呼び出すことができませんT.class
。
それを使用して回避するための好ましい方法は何T.class
ですか?
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<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
}
}
typeParameterClass
コンストラクタでデフォルトの割り当てなしでを割り当てることは、まったく問題ありません。もう一度設定する必要はありません。
クラスパスに追加の依存関係を追加せずに自分でこれを行う方法を探していました。調査の結果、一般的なスーパータイプがあれば可能であることがわかりました。汎用レイヤースーパータイプを使用してDAOレイヤーを操作していたので、これは私にとっては問題ありませんでした。これがあなたのシナリオに当てはまる場合、それは私見の最も近いアプローチです。
私が遭遇したほとんどのジェネリックのユースケースには、たとえばList<T>
for ArrayList<T>
やGenericDAO<T>
for DAO<T>
などのある種のジェネリックスーパータイプがあります。
記事「Javaで実行時にジェネリック型にアクセスする」では、純粋なJavaを使用してそれを行う方法について説明しています。
@SuppressWarnings("unchecked")
public GenericJpaDao() {
this.entityBeanType = ((Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
私のプロジェクトではSpringを使用していましたが、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 ";
}
ただし、小さな抜け穴があります。Foo
クラスを抽象として定義した場合です。つまり、次のようにクラスをインスタンス化する必要があります。
Foo<MyType> myFoo = new Foo<MyType>(){};
(末尾の二重ブレースに注意してください。)
これでT
、実行時にのタイプを取得できます。
Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
ただし、これmySuperclass
はクラス定義のスーパークラスである必要があることに注意してください。T
。
それも非常にエレガントではありませんが、あなたが好むのnew Foo<MyType>(){}
か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 );
}
}
TypeLiteral
a
でb
作成された2つのオブジェクトはどちらも同じクラスを拡張しますが、同一のインスタンスクラスはありません。 a.getClass() != b.getClass()
標準的なアプローチ/回避策/解決策は、次のように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 abstract class Foo<? extends T> {}
そして、Tを拡張するジェネリックBarでFooを拡張する2番目のクラスがあります。
public class Second extends Foo<Bar> {}
(bert bruynoogheの回答から)をBar.class
選択し、インスタンスType
を使用して推論することで、Fooクラスのクラスを取得でき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です。
toString().split(" ")[1]
それが問題でした、避けてください"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 <> ");
}
}
注: スーパークラスとしてのみ使用できます
Child extends Generic<Integer>
)で拡張する必要がありますまたは
new Generic<Integer>() {};
) 型消去のためにそれを行うことはできません。Stack Overflowの質問「Javaジェネリックス-タイプの消去-いつ、どうなるか」も参照してください。
他の人が提案したクラスよりも良いルートは、クラスで実行したことを実行できるオブジェクトを渡すことです。たとえば、新しいインスタンスを作成します。
interface Factory<T> {
T apply();
}
<T> void List<T> make10(Factory<T> factory) {
List<T> result = new ArrayList<T>();
for (int a = 0; a < 10; a++)
result.add(factory.apply());
return result;
}
class FooFactory<T> implements Factory<Foo<T>> {
public Foo<T> apply() {
return new Foo<T>();
}
}
List<Foo<Integer>> foos = make10(new FooFactory<Integer>());
抽象ジェネリッククラスでこの問題が発生しました。この特定のケースでは、解決策はより簡単です。
abstract class Foo<T> {
abstract Class<T> getTClass();
//...
}
以降の派生クラス:
class Bar extends Foo<Whatever> {
@Override
Class<T> getTClass() {
return Whatever.class;
}
}
私はこの問題に対して(醜いが効果的な)解決策を持っています、私は最近使用しました:
import java.lang.reflect.TypeVariable;
public static <T> Class<T> getGenericClass()
{
__<T> ins = new __<T>();
TypeVariable<?>[] cls = ins.getClass().getTypeParameters();
return (Class<T>)cls[0].getClass();
}
private final class __<T> // generic helper class which does only provide type information
{
private __()
{
}
}
それが可能だ:
class Foo<T> {
Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
}
hibernate-generic-dao / blob / master / dao / src / main / java / com / googlecode / genericdao / dao / DAOUtil.javaの 2つの関数が必要です。
詳細については、「総称の反映」を参照してください。
私はそれを行うための一般的で簡単な方法を見つけました。私のクラスでは、クラス定義での位置に従ってジェネリック型を返すメソッドを作成しました。次のようなクラス定義があるとします。
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];
}
}
他の回答で説明されているように、これを使用するには 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();
ジェネリックスを使用しているクラス/インターフェースを拡張または実装する場合、既存のクラス/インターフェースをまったく変更せずに、親クラス/インターフェースのジェネリック型を取得できます。
3つの可能性があります。
ケース1 ジェネリックを使用しているクラスをクラスが拡張している場合
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 クラスがジェネリックスを使用しているインターフェースを実装している場合
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> {
}
それは非常に簡単です。同じクラス内から必要な場合:
Class clazz = this.getClass();
ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
try {
Class typeClass = Class.forName( parameterizedType.getActualTypeArguments()[0].getTypeName() );
// You have the instance of type 'T' in typeClass variable
System.out.println( "Class instance name: "+ typeClass.getName() );
} catch (ClassNotFoundException e) {
System.out.println( "ClassNotFound!! Something wrong! "+ e.getMessage() );
}
実際、T型のクラスにフィールドがあると思います。T型のフィールドがない場合、ジェネリックTypeを持つことの意味は何ですか?したがって、そのフィールドでinstanceofを実行するだけです。
私の場合、私は
リスト<T>アイテム;私のクラスで、私はクラスのタイプが「地方」であるかどうかを確認します
if(items.get(0)instanceof Locality)...
もちろん、これは、可能なクラスの総数が制限されている場合にのみ機能します。
この質問は古いですが、今はグーグルを使用するのが最善です Gson
。
カスタムを取得する例viewModel
。
Class<CustomViewModel<String>> clazz = new GenericClass<CustomViewModel<String>>().getRawType();
CustomViewModel<String> viewModel = viewModelProvider.get(clazz);
ジェネリック型クラス
class GenericClass<T>(private val rawType: Class<*>) {
constructor():this(`$Gson$Types`.getRawType(object : TypeToken<T>() {}.getType()))
fun getRawType(): Class<T> {
return rawType as Class<T>
}
}
Genericsを利用するメソッドにT.classを渡したかった
readFileメソッドは、fileNameで指定された.csvファイルをフルパスで読み取ります。異なる内容のcsvファイルが存在する可能性があるため、適切なオブジェクトを取得できるようにモデルファイルクラスを渡す必要があります。これはcsvファイルを読み込んでいるので、私は一般的な方法でやりたかったです。なんらかの理由で、上記の解決策のどれも私にとってうまくいきませんでした。Class<? extends T> type
それを機能させるために使用する必要があります
。CSVファイルの解析にはopencsvライブラリを使用しています。
private <T>List<T> readFile(String fileName, Class<? extends T> type) {
List<T> dataList = new ArrayList<T>();
try {
File file = new File(fileName);
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
Reader headerReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
CSVReader csvReader = new CSVReader(headerReader);
// create csv bean reader
CsvToBean<T> csvToBean = new CsvToBeanBuilder(reader)
.withType(type)
.withIgnoreLeadingWhiteSpace(true)
.build();
dataList = csvToBean.parse();
}
catch (Exception ex) {
logger.error("Error: ", ex);
}
return dataList;
}
これはreadFileメソッドが呼び出される方法です
List<RigSurfaceCSV> rigSurfaceCSVDataList = readSurfaceFile(surfaceFileName, RigSurfaceCSV.class);
私はこれに回避策を使用しています:
class MyClass extends Foo<T> {
....
}
MyClass myClassInstance = MyClass.class.newInstance();