Genel tip T'nin bir sınıf örneğini nasıl alabilirim?


700

Jenerik dersim var Foo<T>. Bir yöntemde Foo, sınıf türü örneğini almak istiyorum T, ancak sadece arayamıyorum T.class.

Kullanarak dolaşmanın tercih edilen yolu nedir T.class?


2
Bu sorunun cevabını deneyin, bence benzer. stackoverflow.com/questions/1942644/…
Emil



1
import com.fasterxml.jackson.core.type.TypeReference; new TypeReference<T>(){}
Bogdan Shulga

Yanıtlar:


569

Kısa cevap, Java'da genel tür parametrelerinin çalışma zamanı türünü bulmanın bir yolu olmamasıdır. Daha fazla bilgi için Java Öğreticisinde tür silme hakkındaki bölümü okumanızı öneririz .

Buna popüler bir çözüm Class, type parametresini genel türün yapıcısına, örn.

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
    }
}

73
Bu yanıt geçerli bir çözüm sağlar, ancak çalışma zamanında genel türü bulmanın bir yolu olmadığını söylemek yanlıştır. Görünüşe göre tip silme, battaniye silme işleminden çok daha karmaşıktır. Cevabım, sınıfların genel türünü nasıl alacağınızı gösterir.
Ben Thurley

3
@BenThurley Temiz hile, ama görebildiğim kadarıyla sadece kullanmak için genel bir süper tip varsa çalışır. Örneğimde, Foo <T> içinde T türünü alamazsınız.
Zsolt Török

@webjockey Hayır, yapmamalısın. Kurucuya typeParameterClassvarsayılan bir atama olmadan atamak gayet iyi. İkinci kez ayarlamaya gerek yoktur.
Adowrath

Akla gelen ilk çözüm budur, ancak zaman zaman nesneleri yaratacak / başlatacak olan siz değilsiniz. Yani yapıcı kullanamazsınız. Örneğin, veritabanından JPA varlıkları alınırken.
Paramvir Singh Karwal

234

Sınıf yoluna ekstra bir bağımlılık eklemeden bunu kendim yapmanın bir yolunu arıyordum. Bazı araştırmalardan sonra , jenerik bir süpertipiniz olduğu sürece mümkün olduğunu buldum . Genel katman üst türüne sahip bir DAO katmanıyla çalıştığım için bu benim için iyi oldu . Bu sizin senaryonuza uyuyorsa IMHO'nun en güzel yaklaşımıdır.

Çoğu jenerik örn süpertip jenerik bir tür durumlarda Ben geldim kullanmak List<T>için ArrayList<T>ya GenericDAO<T>yönelik DAO<T>vb

Saf Java çözümü

Java'da çalışma zamanında genel türlere erişme makalesi , saf Java kullanarak bunu nasıl yapabileceğinizi açıklar.

@SuppressWarnings("unchecked")
public GenericJpaDao() {
  this.entityBeanType = ((Class) ((ParameterizedType) getClass()
      .getGenericSuperclass()).getActualTypeArguments()[0]);
}

Yay çözümü

Projem, Spring'in türü bulmak için kullanışlı bir yardımcı yöntem olduğu için daha da iyi olan Spring'i kullanıyordu . En güzel göründüğü için bu benim için en iyi yaklaşım. Sanırım Spring'i kullanmadıysan kendi fayda yöntemini yazabilirsin.

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 ";
    }

1
solveTypeArgument arguments
gstackoverflow

getClass (), belirli bir nesnenin sınıfını çalışma zamanında döndürecek bir java.lang.Object yöntemidir, bu, türünü çözmek istediğiniz nesnedir. AbstractHibernatedao.class, yalnızca genel sınıf sınıfı hiyerarşisinin temel sınıfının veya üst sınıfının adıdır. İçe aktarma ifadesi dahil edilmiştir, böylece dokümanları kolayca bulabilir ve kendinizi kontrol edebilirsiniz. Bu sayfa docs.spring.io/spring/docs/current/javadoc-api/org/…
Ben Thurley

4.3.6 ve sonraki sürümlere sahip yay çözümü nedir ? Bahar 4.3.6 ile çalışmaz.
Erlan

1
"Saf Java çözümü" ndeki
Nick Breen

1
@ AlikElzin-kilaka, Spring sınıfı GenericTypeResolver kullanılarak yapıcıda başlatılır.
Ben Thurley

103

Ancak küçük bir boşluk var: Foosınıfınızı soyut olarak tanımlarsanız . Bu, sınıfınızı şu şekilde başlatmanız gerektiği anlamına gelir:

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

(Sondaki çift ayraçlara dikkat edin.)

Artık Tçalışma zamanında türünü alabilirsiniz :

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

Ancak bunun mySuperclassaslında son türünü tanımlayan sınıf tanımının üst sınıfı olması gerektiğini unutmayın T.

Aynı zamanda çok şık değil, ama sen tercih karar vermek zorunda new Foo<MyType>(){}ya new Foo<MyType>(MyType.class);kodunuzda.


Örneğin:

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;
    }
  }
}

Sonra:

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 );
    }
}

5
Bu kolayca burada en iyi cevap! Ayrıca, değerinde, Google Guice'in bağlayıcı sınıflar için kullandığı strateji budurTypeLiteral
ATG

14
Bu nesne oluşturma yöntemi her kullanıldığında, yeni bir anonim sınıf oluşturulduğunu unutmayın. Bir başka deyişle, iki nesne olarak ave bher ikisi de aynı sınıf uzatmak değil aynı örneği sınıfları olacaktır oluşturulan bu şekilde. a.getClass() != b.getClass()
Martin Serrano

3
Bunun işe yaramadığı bir senaryo var. Foo, Serializable gibi bir arabirim uygulamalıysa, sınıf somutlaştırılmadığı sürece anonim sınıf Serializable olmaz. Foo türetilmiş anonim sınıfı oluşturan serileştirilebilir bir fabrika sınıfı oluşturarak geçici çözüm denemeye çalıştım, ancak bazı nedenlerden dolayı getActualTypeArguments gerçek sınıf yerine genel türü döndürür. Örneğin: (yeni FooFactory <MyType> ()). CreateFoo ()
Lior Chaga

38

Standart bir yaklaşım / geçici çözüm / çözüm, classyapıcı (lara) aşağıdaki gibi bir nesne eklemektir :

 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();
    }
 }

1
Ama gerçek kullanımda @awwired, herhangi bir şekilde bir çözüm bulmak mümkün değil gibi görünüyordu?
Alfred Huang

@AlfredHuang Etraftaki çalışma, bunu yapan sınıf için bir fasulye oluşturmak ve otomatik kablolamaya dayanmak olacaktır.
Calebj

20

Genel bir soyut süper sınıfınız olduğunu düşünün:

public abstract class Foo<? extends T> {}

Ve sonra Foo'yu T'yi genişleten genel bir Bar ile genişleten ikinci bir sınıfınız var:

public class Second extends Foo<Bar> {}

Bar.classSınıfı Type(bert bruynooghe yanıtından) seçip Classörneği kullanarak çıkarım yaparak Foo sınıfında alabilirsiniz :

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);

Bu işlemin ideal olmadığını belirtmelisiniz, bu nedenle bu konuda birden fazla hesaplama yapmaktan kaçınmak için hesaplanan değeri önbelleğe almak iyi bir fikirdir. Tipik kullanımlarından biri genel DAO uygulamasındadır.

Son uygulama:

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;
    }
}

Başka bir işlevde Foo sınıfından veya Bar sınıfından çağrıldığında, döndürülen değer Bar.class olur.


1
toString().split(" ")[1]sorun "class "
buydu

16

İşte çalışan bir çözüm:

@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 <> ");
    }
} 

NOTLAR: Yalnızca üst sınıf olarak kullanılabilir

  1. Yazılan sınıf ( Child extends Generic<Integer>) ile genişletilmelidir

VEYA

  1. Anonim uygulama olarak oluşturulmalıdır ( new Generic<Integer>() {};)

3
getTypeName toString'i çağırır, bu nedenle .getActualTypeArguments () [0] .toString ();
Yaroslav Kovbas


9

Diğerlerinin önerdiği Sınıftan daha iyi bir yol, Sınıf ile yaptıklarınızı yapabilen bir nesneye geçmek, örneğin yeni bir örnek oluşturmaktır.

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>());

@ Ricky Clarkson: Bu fabrikanın nasıl parametreli foos döndürmesi gerektiğini anlamıyorum. Foo <T> 'u bundan nasıl çıkaracağınızı açıklar mısınız? Bana öyle geliyor ki bu sadece parametresiz Foo veriyor. Make10'daki T burada sadece Foo değil mi?
ib84

@ ib84 Kodu düzelttim; Cevabı başlangıçta yazarken Foo'nun parametrelendirildiğini kaçırmışım gibi görünüyor.
Ricky Clarkson

9

Bu sorunu soyut bir genel sınıfta yaşadım. Bu özel durumda, çözüm daha basittir:

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

ve daha sonra türetilmiş sınıfta:

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

Evet, ama bu sınıfı genişletirken yapılması gereken çok az bırakmak istiyorum. Droidpl'in cevabını kontrol edin
Paramvir Singh Karwal

5

Son zamanlarda kullandığım bu sorun için (çirkin ama etkili) bir çözümüm var:

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 __()
    {
    }
}


3

Bunu yapmanın genel ve basit bir yolunu buldum. Sınıfımda, sınıf türündeki konumuna göre genel türü döndüren bir yöntem oluşturdum. Bunun gibi bir sınıf tanımını varsayalım:

public class MyClass<A, B, C> {

}

Şimdi türleri devam ettirmek için bazı özellikler oluşturalım:

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)

    } 

Ardından, genel tanımın dizinine göre türü döndüren genel bir yöntem oluşturabilirsiniz:

   /**
     * 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];
    }

Son olarak, yapıcı sadece yöntemi çağırmak ve her tür için dizin göndermek. Kodun tamamı şöyle görünmelidir:

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];
    }
}

2

Diğer cevaplarda açıklandığı gibi, bu ParameterizedTypeyaklaşımı kullanmak için, sınıfı genişletmeniz gerekir, ancak bu, onu genişleten yepyeni bir sınıf yapmak için ekstra bir iş gibi görünüyor ...

Böylece, sınıfı soyutlaştırmak, onu genişletmeye zorlar, böylece alt sınıf gereksinimini karşılar. (lombok's @Getter kullanarak).

@Getter
public abstract class ConfigurationDefinition<T> {

    private Class<T> type;
    ...

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

Şimdi yeni bir sınıf tanımlamadan genişletmek için. (Sonunda {} uzunluğunu not edin ... ancak istemediğiniz sürece hiçbir şeyin üzerine yazmayın).

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

2

Genel bir sınıf olduğundan, böyle bir değişken olacağını varsayalım:

private T t;

(bu değişkenin yapıcıda bir değer alması gerekir)

Bu durumda, aşağıdaki yöntemi oluşturabilirsiniz:

Class<T> getClassOfInstance()
{
    return (Class<T>) t.getClass();
}

Umarım yardımcı olur!


1
   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
        }
   }

1

Jenerikler kullanan herhangi bir sınıfı / arabirimi genişletiyor veya uyguluyorsanız, varolan herhangi bir sınıfı / arabirimi değiştirmeden Genel Type üst sınıf / arabirim türünü alabilirsiniz.

Üç olasılık olabilir,

Durum 1 Sınıfınız Generics kullanan bir sınıfı genişletiyorsa

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> {
}

Durum 2 Sınıfınız Generics kullanan bir arabirim uygularken

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){}
}

Durum 3 Arayüzünüz Generics kullanan bir arayüzü genişletiyorsa

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> {
}

1

Bu oldukça basit. Aynı sınıftan ihtiyacınız varsa:

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() );
    }

0

Aslında, T tipi sınıfınızda bir alanınız olduğunu varsayalım T tipi bir alan yoksa, genel bir Type'a sahip olmanın anlamı nedir? Böylece, bu alanda bir örnek oluşturabilirsiniz.

Benim durumumda, bir

<T> öğelerini listeleyin;
ve sınıf türünün "Locality" olup olmadığını

if (items.get (0) Yerellik durumu) ...

Tabii ki, bu sadece olası sınıfların toplam sayısı sınırlıysa işe yarar.


4
İtems.isEmpty () doğruysa ne yapmalıyım?
chaotic3quilibrium

0

Bu soru eski, ama şimdi en iyisi google'ı kullanmak Gson .

Özel bir örnek viewModel.

Class<CustomViewModel<String>> clazz = new GenericClass<CustomViewModel<String>>().getRawType();
CustomViewModel<String> viewModel = viewModelProvider.get(clazz);

Genel tip sınıfı

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>
    }
}

0

T.class'ı Generics'ten faydalanan bir yönteme aktarmak istedim

ReadFile yöntemi , tam yol ile fileName tarafından belirtilen bir .csv dosyasını okur. Farklı içeriklere sahip csv dosyaları olabilir, bu yüzden uygun nesneleri alabilmem için model dosya sınıfını geçmem gerekir. Bu csv dosyasını okuduğu için genel bir şekilde yapmak istedim. Bir nedenden ötürü veya yukarıdaki çözümlerin hiçbiri benim için işe yaramadı. Çalıştırmak için kullanmam gerekiyor Class<? extends T> type. CSV dosyalarını ayrıştırmak için opencsv kütüphanesini kullanıyorum.

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 yöntemi bu şekilde çağrılır

List<RigSurfaceCSV> rigSurfaceCSVDataList = readSurfaceFile(surfaceFileName, RigSurfaceCSV.class);

-4

Bunun için geçici çözüm kullanıyorum:

class MyClass extends Foo<T> {
....
}

MyClass myClassInstance = MyClass.class.newInstance();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.