Dapatkan tipe generik kelas saat runtime

508

Bagaimana saya bisa mencapai ini?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Semua yang saya coba sejauh ini selalu mengembalikan tipe Objectdaripada tipe spesifik yang digunakan.

Glenn
sumber
3
@SamuelVidal Ini bukan .NET, ini Java.
Frontear

Jawaban:

317

Seperti yang disebutkan orang lain, itu hanya mungkin melalui refleksi dalam keadaan tertentu.

Jika Anda benar-benar membutuhkan jenisnya, ini adalah pola penyelesaian (jenis-aman) yang biasa:

public class GenericClass<T> {

     private final Class<T> type;

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

     public Class<T> getMyType() {
         return this.type;
     }
}
Henning
sumber
58
Saya suka jawaban ini tetapi agak rumit untuk instantiate: GenericClass <AnotherClass> g = GenericClass baru <AnotherClass> (AnotherClass.class);
Eliseo Ocampos
1
Bahkan lebih verbose jika Anda menggunakan pendekatan dao / pabrik / manajer. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
djmj
2
Itu benar, tetapi tidak berfungsi dalam semua kasus seperti kacang jarak jauh stateless yang dipakai oleh wadah / refleksi.
djmj
6
Sama seperti tindak lanjut dari komentar saya sebelumnya - setelah banyak kesakitan bermain dengan refleksi, saya akhirnya menggunakan jawaban ini.
Tomáš Zato - Kembalikan Monica
18
Anda dapat berkeliling referensi berlebihan dengan menyediakan metode pabrik statis generik. Sesuatu seperti public static <T> GenericClass<T> of(Class<T> type) {...}dan kemudian menyebutnya seperti: GenericClass<String> var = GenericClass.of(String.class). Agak lebih bagus.
Joeri Hendrickx
263

Saya telah melihat sesuatu seperti ini

private Class<T> persistentClass;

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

dalam Contoh hibernate GenericDataAccessObjects

FrVaBe
sumber
84
Teknik ini bekerja di mana parameter tipe didefinisikan pada superclass langsung, tetapi gagal jika parameter tipe didefinisikan di tempat lain dalam hirarki tipe. Untuk menangani kasus yang lebih kompleks, sesuatu seperti TypeTools dapat digunakan. Dokumen mencakup contoh DAO Generik yang lebih canggih.
Jonathan
25
Ini hanya mengembalikan parameter tipe aktual yang digunakan ketika CLASS mengimplementasikan / memperluas sesuatu yang memiliki deklarasi generik, itu tidak mengembalikan parameter tipe aktual yang digunakan ketika INSTANSI dipakai. Dengan kata lain, ia BISA mengatakan bahwa dalam class A implements Comparable<String>, parameter tipe sebenarnya String, tetapi TIDAK BISA mengatakan bahwa dalam Set<String> a = new TreeSet<String>(), parameter tipe aktual adalah String. Bahkan, informasi parameter tipe "dihapus" setelah kompilasi, seperti yang dijelaskan dalam jawaban lain.
Siu Ching Pong -Asuka Kenji-
32
Saya mendapatkan java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypejawaban ini.
Tomáš Zato - Kembalikan Monica
4
Pendekatan ini juga dapat dicapai menggunakan Class-Matedari orang-orang Jackson. Saya menulis sebuah intisari di sini gist.github.com/yunspace/930d4d40a787a1f6a7d1
yunspace
5
@ TomášZato Memanggil hanya kode di atas mengembalikan pengecualian yang sama untuk saya. Saya tahu ini agak terlambat, tetapi bagaimanapun, dalam kasus saya, saya harus menelepon (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()untuk mendapatkan argumen tipe yang sebenarnya.
AndrewMcCoist
99

Generik tidak diverifikasi pada saat run-time. Ini berarti informasi tersebut tidak ada pada saat dijalankan.

Menambahkan obat generik ke Java sambil mempertahankan kompatibilitas ke belakang adalah tur-de-force (Anda dapat melihat makalah mani tentang hal itu: Membuat masa depan aman untuk masa lalu: menambahkan kedermawanan ke bahasa pemrograman Java ).

Ada literatur yang kaya tentang masalah ini, dan beberapa orang tidak puas dengan keadaan saat ini, beberapa mengatakan bahwa sebenarnya itu adalah daya tarik dan tidak ada kebutuhan nyata untuk itu. Anda dapat membaca kedua tautan, saya menemukan mereka cukup menarik.

ewernli
sumber
179
Tentu saja kami tidak puas, .NET memiliki mekanisme penanganan generik yang jauh lebih baik
Pacerier
6
@ Peracerier: tetapi reified generic saja tidak akan membawa Java ke tingkat. Jenis nilai kode khusus untuk mereka setidaknya sama pentingnya untuk mengapa .NET lebih baik di area generik.
Joachim Sauer
@ JoachimSauer, ya tipe nilai. Saya selalu menginginkan yang ada di java. Btw apa maksudmu dengan kode khusus?
Pacerier
3
@ spaaarky21 Tidak, parameter tipe generik dihapus selama kompilasi (disebut "erasure", Anda dapat google itu). Trik dalam jawaban FrVaBe hanya berfungsi jika param tipe superclass diketahui secara statis (lihat komitmen pertama oleh Johnathn)
ewernli
1
Penghapusan tipe Java adalah cacat desain historis; lebih banyak kode telah ditulis untuk mengatasinya daripada ditulis untuk mengimplementasikannya.
Abhijit Sarkar
68

Gunakan Jambu Biji.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

Beberapa waktu yang lalu, saya memposting beberapa contoh lengkap termasuk kelas abstrak dan subclass di sini .

Catatan: ini mengharuskan Anda instantiate subclass dari GenericClasssehingga dapat mengikat jenis parameter dengan benar. Kalau tidak, itu hanya akan mengembalikan tipe T.

Cody A. Ray
sumber
3
Perhatikan bahwa saya membuat subkelas anonim kosong (lihat dua kurung kurawal di akhir). Ini menggunakan refleksi untuk melawan penghapusan tipe runtime Java. Anda dapat mempelajari lebih lanjut di sini: code.google.com/p/guava-libraries/wiki/ReflectionExplained
Cody A. Ray
3
@ CodyA.Ray Kode Anda melempar a java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. Jadi saya mengubah jalur new TypeToken(getClass()) { }menjadi new TypeToken<T>(getClass()) { }. Sekarang, kode berjalan dengan baik, tetapi Type masih 'T'. Lihat ini: gist.github.com/m-manu/9cda9d8f9d53bead2035
Manu Manjunath
3
@Dominik Silakan lihat contoh yang diperbarui yang dapat Anda salin dan tempel untuk menguji diri Anda. Saya juga menambahkan catatan yang menjelaskan bahwa Anda harus membuat instance subclass (seperti yang ditunjukkan). Sebagai saran etiket umum, silakan baca artikel terkait dan javadocs terkait sebelum Anda menuduh poster "angan-angan". Saya telah menggunakan kode produksi serupa beberapa kali. Pembantu Guava yang saya tunjukkan dimaksudkan untuk kasus penggunaan yang tepat ini dan javadocs mereka menunjukkan jawaban yang hampir pasti untuk pertanyaan ini. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Cody A. Ray
1
Jawaban ini berfungsi baik dengan Java8 - Saya memiliki antarmuka dan kelas pabrik untuk memancarkan berbagai jenis, dan ingin pengguna dapat dengan mudah menentukan jenisnya. Dalam antarmuka saya, saya menambahkan metode default: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Richard Sand
2
@ CodyA.Ray Karena ini hanya bekerja dengan subkelas GenericClass, Anda harus membuat kelas itu abstractsehingga penggunaan yang salah tidak dapat dikompilasi.
Martin
37

Tentu kamu bisa.

Java tidak menggunakan informasi pada saat dijalankan, untuk alasan kompatibilitas ke belakang. Tetapi informasi tersebut sebenarnya hadir sebagai metadata dan dapat diakses melalui refleksi (tetapi masih tidak digunakan untuk pengecekan tipe).

Dari API resmi:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

Namun , untuk skenario Anda, saya tidak akan menggunakan refleksi. Saya pribadi lebih cenderung menggunakannya untuk kode kerangka kerja. Dalam kasus Anda, saya hanya akan menambahkan jenis sebagai param konstruktor.

Joeri Hendrickx
sumber
10
getActualTypeArguments hanya mengembalikan argumen tipe untuk kelas langsung. Jika Anda memiliki hierarki tipe yang kompleks di mana T dapat parameter di mana saja dalam hierarki, Anda harus melakukan sedikit pekerjaan untuk mengetahui apa itu. Ini kurang lebih apa yang dilakukan TypeTools .
Jonathan
36

Java generics sebagian besar waktu kompilasi, ini berarti bahwa informasi jenis hilang saat runtime.

class GenericCls<T>
{
    T t;
}

akan dikompilasi menjadi sesuatu seperti

class GenericCls
{
   Object o;
}

Untuk mendapatkan informasi jenis saat runtime Anda harus menambahkannya sebagai argumen dari ctor.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

Contoh:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
Josefx
sumber
9
private final Class<T> type;
naXa
Bagaimana saya bisa membuat tipe array darinya:Type t = //String[]
Pawel Cioch
@PawelCioch java.lang.reflect.Array.newInstance (elementtype, length); Semoga ini bisa membantu (javadoc dapat ditemukan di sini docs.oracle.com/javase/8/docs/api/java/lang/reflect/… )
josefx
@PawelCioch melewatkan .getClass () untuk mendapatkan tipe dari array yang dibuat. Tampaknya tidak ada cara langsung untuk mendapatkan kelas array. Sebagian besar koleksi Java hanya menggunakan Object [].
josefx
23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
Ali Yeganeh
sumber
3
Saya memilih jawaban ini karena ini solusi yang berfungsi untuk pertanyaan yang diajukan. Namun, bagi mereka yang ingin menavigasi ke atas dalam hierarki kelas seperti saya dengan lebih dari satu kelas Generik, ini tidak akan berhasil. Karena Anda akan mendapatkan java.lang.object daripada kelas yang sebenarnya.
KMC
3
Harap dicatat bahwa solusi ini HANYA jika kelas yang memiliki tipe generik adalah ABSTRAK
BabaNew
Tidak berfungsi dengan kelas abstrak saya di Java 11
JRA_TLL
@JRA_TLL Anda ternyata melakukan kesalahan. Saya hanya menggunakannya dengan Java 12 dan bekerja seperti pesona.
Googie
Jika Anda ingin menavigasi dalam hierarki tampilan, Anda dapat menggunakan genericSuperclass ke Class <*> dan mendapatkan genericSuperclass. Lebih disukai dalam satu lingkaran.
fupduck
21

Saya menggunakan pendekatan follow:

public class A<T> {

    protected Class<T> clazz;

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

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}
Moesio
sumber
9
Saya mendapat "Pengecualian di utas" utama "java.lang.ClassCastException: java.lang.Class tidak dapat dilemparkan ke java.lang.reflect.ParameterizedType" dengan kode contoh ini
yuyang
16

Saya rasa Anda tidak bisa, Java menggunakan tipe erasure saat kompilasi sehingga kode Anda kompatibel dengan aplikasi dan pustaka yang dibuat pra-generik.

Dari Oracle Docs:

Ketik Penghapusan

Generik diperkenalkan ke bahasa Jawa untuk menyediakan pemeriksaan tipe yang lebih ketat pada waktu kompilasi dan untuk mendukung pemrograman generik. Untuk menerapkan obat generik, kompiler Java menerapkan tipe penghapusan untuk:

Ganti semua parameter tipe dalam tipe generik dengan batas atau Objek mereka jika parameter tipe tidak terikat. Bytecode yang dihasilkan, oleh karena itu, hanya berisi kelas, antarmuka, dan metode biasa. Masukkan gips tipe jika perlu untuk menjaga keamanan tipe. Hasilkan metode jembatan untuk melestarikan polimorfisme dalam tipe generik yang diperluas. Penghapusan tipe memastikan bahwa tidak ada kelas baru yang dibuat untuk tipe parameter; akibatnya, obat generik tidak menimbulkan overhead runtime.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Dimitar
sumber
Jup, tidak mungkin. Java akan membutuhkan generik terverifikasi untuk bisa berfungsi.
Henning
15

Teknik yang dijelaskan dalam artikel ini oleh Ian Robertson bekerja untuk saya.

Singkatnya, contoh cepat dan kotor:

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }
Ondrej Bozek
sumber
3
Di baris spesifik mana dalam kode ini yang sebenarnya, parameter tipe run-time sedang dibaca?
Ivan Matavulj
sini? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ondrej Bozek
9

Saya pikir ada solusi elegan lain.

Apa yang ingin Anda lakukan adalah (dengan aman) "meneruskan" tipe parameter tipe generik dari kelas concerete ke superclass.

Jika Anda membiarkan diri Anda memikirkan tipe kelas sebagai "metadata" di kelas, itu menyarankan metode Java untuk menyandikan metadata di saat runtime: annotations.

Pertama-tama tentukan anotasi khusus di sepanjang baris ini:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

Anda kemudian harus menambahkan anotasi ke subkelas Anda.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

Kemudian Anda bisa menggunakan kode ini untuk mendapatkan tipe kelas di kelas dasar Anda:

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

Beberapa batasan dari pendekatan ini adalah:

  1. Anda menentukan tipe generik ( PassedGenericType) di DUA tempat daripada yang non-KERING.
  2. Ini hanya mungkin jika Anda dapat memodifikasi subkelas beton.
DaBlick
sumber
Ya, ini bukan KERING, namun lebih bersih daripada pendekatan ekstensi yang disarankan di atas. Aku menyukainya. terima kasih
agodinhost
8

Ini solusi saya:

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}
Matthias M
sumber
10
Ini bukan jawaban untuk pertanyaan ini.
Adam Arold
1
Kode saya bukan solusi tepat untuk pertanyaan itu. Ini mengembalikan parameter tipe generik dari kelas, tetapi bukan tipe T. yang sebenarnya. Tetapi mungkin bermanfaat bagi orang lain yang mencari-cari pertanyaan dan mencari solusi saya.
Matthias M
1
getClass (). getGenericSuperclass () akan mencapai efek yang sama.
Gorky
5

Inilah solusi yang berfungsi !!!

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

CATATAN: Dapat digunakan hanya sebagai superclass
1. Harus diperluas dengan kelas yang diketik ( Child extends Generic<Integer>)
ATAU

2. Harus dibuat sebagai implementasi anonim ( new Generic<Integer>() {};)

Yaroslav Kovbas
sumber
4

Kamu tidak bisa Jika Anda menambahkan variabel anggota tipe T ke kelas (Anda bahkan tidak harus menginisialisasi), Anda bisa menggunakannya untuk memulihkan jenis.

andrewmu
sumber
2
Ups, oke. Anda tidak harus menginisialisasinya dari sebuah konstruktor.
andrewmu
4

Inilah satu cara, yang harus saya gunakan sekali atau dua kali:

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

Bersama

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}
PJWeisberg
sumber
4
Secara teknis ini berfungsi, namun itu tidak menyelesaikan kasus umum, dan saya pikir itulah yang dicari oleh poster aslinya.
ggb667
Ini tidak pantas untuk ditolak seperti yang telah - poster asli belum eksplisit. Jawaban ini menawarkan pola desain yang tidak bekerja dan mudah untuk menerapkan, disediakan sangat cocok untuk membuat kelas generik abstrak.
VirtualMichael
4

Salah satu solusi sederhana untuk taksi ini adalah seperti di bawah ini

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}
Paul92
sumber
3

Untuk menyelesaikan beberapa jawaban di sini, saya harus mendapatkan ParametrizedType dari MyGenericClass, tidak peduli seberapa tinggi hierarki, dengan bantuan rekursi:

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}
galex
sumber
1

Ini trik saya:

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}
HRgiger
sumber
1
Catatan: ini tidak berfungsi ketika Tmerupakan variabel tipe. Dalam kasus yang Tmerupakan variabel tipe, varargs menciptakan array penghapusan T. Lihat misalnya http://ideone.com/DIPNwd .
Radiodef
1
Ini mengembalikan "Objek"
yahya
Mungkin Anda mencoba menjawab beberapa pertanyaan lain 🤔
Khan
1

Hanya dalam kasus Anda menggunakan menyimpan variabel menggunakan tipe generik Anda dapat dengan mudah menyelesaikan masalah ini menambahkan metode getClassType sebagai berikut:

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

Saya menggunakan objek kelas yang disediakan nanti untuk memeriksa apakah itu adalah turunan dari kelas yang diberikan, sebagai berikut:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}
Pablo Trinidad
sumber
2
Sayangnya, ini bermasalah. Pertama-tama, bagaimana jika valueitu null? Kedua, bagaimana jika valuesubkelas T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();kembali Integer.classketika seharusnya kembali Number.class. Akan lebih benar untuk kembali Class<? extends T>. Integer.class adalah Class<? extends Number> tetapi tidak a Class<Number>.
Radiodef
1

Ini solusinya

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

Tidak peduli berapa banyak level hierarki kelas Anda, solusi ini tetap berfungsi, misalnya:

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

Dalam hal ini, getMyType () = java.lang.String

Lin Yu Cheng
sumber
Ini bukan mengembalikan tipe T. Itu mengembalikan T bukan java.lang.String selain kode gagal untuk Tipe rahasia ke Kelas <T>
Mohy Eldeen
Ini adalah contoh online yang saya buat. Klik kompilasi dan jalankan, maka Anda bisa mendapatkan hasilnya. tutorialspoint.com/...
Lin Yu Cheng
Bekerja untuk saya - ketika WildFly Weld CDI memecahkan metode alternatif.
Andre
Saya mendapatException in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
yuyang
0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
kayz1
sumber
0

Ini solusinya. Contoh harus menjelaskannya. Satu-satunya persyaratan adalah bahwa subkelas harus menetapkan tipe generik, bukan objek.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}
PetermaxX
sumber
-1

Saya melakukan hal yang sama dengan @ Mario di atas tetapi di Kotlin bisa dilakukan dengan cara ini:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}
Bonestack
sumber
-4

Mungkin bermanfaat bagi seseorang. Anda dapat Menggunakan java.lang.ref.WeakReference; cara ini:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}
Ambil
sumber
Bagaimana kelas ini seharusnya digunakan? Mengapa WeakReference? Harap berikan penjelasan dengan jawaban Anda, bukan hanya beberapa kode.
Abhijit Sarkar
Jadi, jika Anda memiliki, SomeClass<MyClass> Anda dapat instantiate SomeClassdan memanggil getTypeinstance itu dan memiliki runtime menjadi MyClass.
Ambil
Tentu, tapi mengapa WeakReference? Apa yang Anda katakan tidak berbeda dari sebagian besar jawaban lainnya.
Abhijit Sarkar
Pertama pendekatan saya lebih pendek (kode kurang), kedua referensi Lemah tidak mencegah referensi mereka menjadi dapat diselesaikan, dan sejauh yang saya tahu itu tidak menggunakan refleksi, jadi itu cepat
TheLetch
Ini tidak mendapatkan jenis apa pun, ini mengembalikan sebuah objek dari tipe itu, yang, fyi, dapat Anda lakukan dengan harfiah apapun wrapper ( AtomicReference, List, Set).
Frontear
-5

Saya menemukan ini sebagai solusi yang mudah dimengerti dan mudah dijelaskan

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}
Mark Karchner
sumber
Jelaskan (T...t). (Itulah sebabnya kode ini tidak berfungsi.)
Christopher Schneider