Yanıtlar:
Diğerlerinin de belirttiği gibi, sadece belirli durumlarda yansıma yoluyla mümkündür.
Gerçekten türe ihtiyacınız varsa, bu her zamanki (tipte güvenli) geçici çözüm şeklidir:
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) {...}
ve sonra gibi diyoruz: GenericClass<String> var = GenericClass.of(String.class)
. Biraz daha hoş.
Böyle bir şey gördüm
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
içinde hazırda GenericDataAccessObjects Örnek
class A implements Comparable<String>
, gerçek tip parametresidir String
, ancak CAN NOT içinde söylemek Set<String> a = new TreeSet<String>()
, gerçek tip parametredir String
. Aslında, tür parametre bilgileri, diğer cevaplarda açıklandığı gibi, derlemeden sonra "silinir".
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
Bu cevabı alacağım .
(Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()
gerçek tür argümanlarına ulaşmak için çağırmak zorunda kaldım.
Jenerik değildir şeyleşmiş çalışma anında. Bu, bilginin çalışma zamanında mevcut olmadığı anlamına gelir.
Geriye dönük uyumluluğu korurken Java'ya jenerikler eklemek bir tur-güçtü (bununla ilgili seminal makaleyi görebilirsiniz: Geleceği geçmiş için güvenli kılmak: Java programlama diline jeneriklik eklemek ).
Konuyla ilgili zengin bir literatür var ve bazı insanlar mevcut durumdan memnun değil , bazıları aslında bir cazibesi olduğunu ve buna gerçek bir ihtiyaç olmadığını söylüyor . Her iki bağlantıyı da okuyabilirsiniz, onları oldukça ilginç buldum.
Guava kullanın.
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
}
}
Bir süre önce, ben soyut sınıflar ve alt sınıflar da dahil olmak üzere bazı tam fledge örneklerini yayınlanmıştır burada .
Not: Bu bir örneğini gerektirir alt sınıf arasında GenericClass
doğru türü parametresi bağlamak böylece. Aksi takdirde, türü yalnızca olarak döndürür T
.
java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized
. Bu yüzden hattı new TypeToken(getClass()) { }
olarak değiştirdim new TypeToken<T>(getClass()) { }
. Şimdi, kod iyi çalışıyor, ancak Type hala 'T'. Şuna
default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
GenericClass
, bu sınıfı abstract
yanlış kullanımın derlememesi için yapmalısınız .
Tabi ki yapabilirsin.
Java, geriye dönük uyumluluk nedeniyle bilgileri çalışma zamanında kullanmaz . Ama bilgi aslında mevcut meta veri olarak mevcuttur ve yansıma yoluyla erişilebilir (ancak yine de tür denetimi için kullanılmaz).
Resmi API'dan:
Ancak , senaryonuz için yansımayı kullanmam. Şahsen bunu çerçeve kodu için kullanmaya meyilliyim. Senin durumunda sadece bir yapıcı parametre olarak eklemek istiyorum.
Java jenerikleri çoğunlukla derleme süresidir, bu da tür bilgisinin çalışma zamanında kaybolduğu anlamına gelir.
class GenericCls<T>
{
T t;
}
gibi bir şeye derlenecek
class GenericCls
{
Object o;
}
Çalışma zamanında tür bilgilerini almak için bunu ctor argümanı olarak eklemeniz gerekir.
class GenericCls<T>
{
private Class<T> type;
public GenericCls(Class<T> cls)
{
type= cls;
}
Class<T> getType(){return type;}
}
Misal:
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];
}
}
Takip yaklaşımını kullandım:
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();
}
}
Yapabileceğinizi sanmıyorum, Java derleme yaparken tür silme kullanır, böylece kodunuz jenerikler tarafından oluşturulan uygulamalar ve kütüphanelerle uyumludur.
Oracle Dokümanlar'dan:
Türü Silme
Derleme zamanında daha sıkı tip kontroller sağlamak ve genel programlamayı desteklemek için Java diline jenerikler tanıtıldı. Jenerikleri uygulamak için Java derleyicisi aşağıdakilere tür silme uygular:
Genel türlerdeki tüm tür parametrelerini sınırları ile veya tür parametreleri sınırsızsa Object ile değiştirin. Bu nedenle üretilen bayt kodu sadece sıradan sınıflar, arayüzler ve yöntemler içerir. Tip güvenliğini korumak için gerekirse tip kalıpları yerleştirin. Genişletilmiş jenerik tiplerde polimorfizmi korumak için köprü yöntemleri oluşturun. Tür silme, parametreli türler için yeni sınıf oluşturulmamasını sağlar; sonuç olarak, jenerikler çalışma zamanı yükü oluşturmazlar.
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
Ian Robertson tarafından bu makalede açıklanan teknik benim için çalışıyor.
Kısaca hızlı ve kirli örnek:
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();
Bence başka bir zarif çözüm daha var.
Yapmak istediğiniz şey (güvenli bir şekilde) genel tip parametresinin türünü concete sınıfından süper sınıfa "geçirmektir".
Sınıf türünü sınıfta "meta veri" olarak düşünmenize izin verirseniz, bu, çalışma zamanında meta verileri kodlamak için Java yöntemini gösterir: ek açıklamalar.
İlk olarak şu satırlar boyunca özel bir ek açıklama tanımlayın:
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
Class entityClass();
}
Daha sonra ek açıklamayı alt sınıfınıza eklemeniz gerekebilir.
@EntityAnnotation(entityClass = PassedGenericType.class)
public class Subclass<PassedGenericType> {...}
Daha sonra temel sınıfınızda sınıf türünü almak için bu kodu kullanabilirsiniz:
import org.springframework.core.annotation.AnnotationUtils;
.
.
.
private Class getGenericParameterType() {
final Class aClass = this.getClass();
EntityAnnotation ne =
AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);
return ne.entityClass();
}
Bu yaklaşımın bazı sınırlamaları:
PassedGenericType
) DRY olmayan bir tane yerine İKİ yerde .Bu benim çözümüm:
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);
}
}
}
}
İşte çalışma çö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
1 olarak kullanılabilir. Yazılan sınıf ( Child extends Generic<Integer>
)
ile genişletilmesi gerekir
VEYA
2. Anonim uygulama ( new Generic<Integer>() {};
) olarak oluşturulması gerekir
İşte bir veya iki kez kullanmam gereken bir yol:
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
İle birlikte
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
Bu kabin için basit bir çözüm aşağıdaki gibi olabilir
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());
}
}
Burada bazı cevapları tamamlamak için, özyineleme yardımı ile hiyerarşi ne kadar yüksek olursa olsun, MyGenericClass ParametrizedType almak zorunda kaldı:
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());
}
}
İşte benim hile:
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
. T
Bir tür değişkeni olması durumunda , varargs bir silme dizisi oluşturur T
. Bkz. Örneğin http://ideone.com/DIPNwd .
Genel türü kullanarak bir değişkeni depolamanız durumunda, getClassType yöntemini aşağıdaki gibi ekleyerek bu sorunu kolayca çözebilirsiniz:
public class Constant<T> {
private T value;
@SuppressWarnings("unchecked")
public Class<T> getClassType () {
return ((Class<T>) value.getClass());
}
}
Aşağıdaki gibi verilen bir sınıf örneği olup olmadığını denetlemek için sağlanan sınıf nesnesini daha sonra aşağıdaki gibi kullanın:
Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
Constant<Integer> integerConstant = (Constant<Integer>)constant;
Integer value = integerConstant.getValue();
// ...
}
value
ise null
? İkincisi, ya value
bir alt sınıf ise T
? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();
geri Integer.class
dönmesi gerektiğinde döner Number.class
. Geri dönmek daha doğru olurdu Class<? extends T>
. Integer.class
bir Class<? extends Number>
ama değil a Class<Number>
.
İşte benim çözümüm
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;
}
}
Sınıf hiyerarşiniz kaç seviye olursa olsun, bu çözüm hala işe yarar, örneğin:
public class FirstLevelChild<T> extends GenericClass<T> {
}
public class SecondLevelChild extends FirstLevelChild<String> {
}
Bu durumda, 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)
İşte benim çözümüm. Örnekler bunu açıklamalıdır. Tek gereksinim, bir alt sınıfın bir nesne değil, genel türü ayarlaması gerektiğidir.
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;
}
}
Birisi için yararlı olabilir. Java.lang.ref.WeakReference kullanabilirsiniz; bu yoldan:
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;
N getType(){
return variableToGetTypeFrom.get();
}
}
WeakReference
? Lütfen sadece bazı kodları değil cevabınızı biraz açıklayınız.
SomeClass<MyClass>
örneğiniz varsa, bu örneği başlatabilir SomeClass
ve çağırabilirsiniz getType
ve çalışma zamanına sahip olabilirsiniz MyClass
.
WeakReference
? Söyledikleriniz diğer cevapların çoğundan farklı değil.
AtomicReference
, List
, Set
).
Bunu basit, anlaşılır ve kolayca açıklanabilir bir çözüm olarak buldum
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)
. (Bu yüzden bu kod çalışmaz.)