Jawaban:
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;
}
}
Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
public static <T> GenericClass<T> of(Class<T> type) {...}
dan kemudian menyebutnya seperti: GenericClass<String> var = GenericClass.of(String.class)
. Agak lebih bagus.
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
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.
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
jawaban ini.
Class-Mate
dari orang-orang Jackson. Saya menulis sebuah intisari di sini gist.github.com/yunspace/930d4d40a787a1f6a7d1
(Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()
untuk mendapatkan argumen tipe yang sebenarnya.
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.
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 GenericClass
sehingga dapat mengikat jenis parameter dengan benar. Kalau tidak, itu hanya akan mengembalikan tipe T
.
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
default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
GenericClass
, Anda harus membuat kelas itu abstract
sehingga penggunaan yang salah tidak dapat dikompilasi.
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:
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.
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;
private final Class<T> type;
Type t = //String[]
public abstract class AbstractDao<T>
{
private final Class<T> persistentClass;
public AbstractDao()
{
this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
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();
}
}
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
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;
}
}
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
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:
PassedGenericType
) di DUA tempat daripada yang non-KERING.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);
}
}
}
}
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>() {};
)
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;
}
}
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());
}
}
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());
}
}
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();
}
}
T
merupakan variabel tipe. Dalam kasus yang T
merupakan variabel tipe, varargs menciptakan array penghapusan T
. Lihat misalnya http://ideone.com/DIPNwd .
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();
// ...
}
value
itu null
? Kedua, bagaimana jika value
subkelas T
? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();
kembali Integer.class
ketika seharusnya kembali Number.class
. Akan lebih benar untuk kembali Class<? extends T>
. Integer.class
adalah Class<? extends Number>
tetapi tidak a Class<Number>
.
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
Exception 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)
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;
}
}
Mungkin bermanfaat bagi seseorang. Anda dapat Menggunakan java.lang.ref.WeakReference; cara ini:
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;
N getType(){
return variableToGetTypeFrom.get();
}
}
WeakReference
? Harap berikan penjelasan dengan jawaban Anda, bukan hanya beberapa kode.
SomeClass<MyClass>
Anda dapat instantiate SomeClass
dan memanggil getType
instance itu dan memiliki runtime menjadi MyClass
.
WeakReference
? Apa yang Anda katakan tidak berbeda dari sebagian besar jawaban lainnya.
AtomicReference
, List
, Set
).
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);
}
}
(T...t)
. (Itulah sebabnya kode ini tidak berfungsi.)