Відповіді:
Як згадували інші, це можливо лише через рефлексію в певних обставинах.
Якщо вам дійсно потрібен тип, це звичайний (безпечний для типу) шаблон вирішення:
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) {...}
а потім викликати її як таку: GenericClass<String> var = GenericClass.of(String.class)
. Трохи приємніше.
Я бачив щось подібне
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
у прикладі сплячого загального ДаніAccessObjects
class A implements Comparable<String>
, фактичний параметр типу є String
, але НЕ МОЖЕ сказати, що в Set<String> a = new TreeSet<String>()
, фактичний параметр типу є String
. Фактично, інформація про параметри типу "стирається" після компіляції, як пояснено в інших відповідях.
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
за цю відповідь.
(Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()
щоб перейти до фактичних аргументів типу.
Дженерики НЕ матеріалізований під час виконання. Це означає, що інформація відсутня під час виконання.
Додавання дженериків до Java під час збереження відсталої сумісності було справжньою умовою (ви можете ознайомитись з настільним документом про це: Зробити майбутнє безпечним для минулого: додавши щедрість мові програмування Java ).
Існує багата література на цю тему, і деякі люди незадоволені сучасним станом, деякі кажуть, що насправді це приманка і в цьому немає реальної потреби. Ви можете прочитати обидва посилання, я вважав їх досить цікавими.
Використовуйте Гуаву.
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
}
}
Деякий час назад, я відправив кілька прикладів повноцінних включаючи абстрактні класи і підкласи тут .
Примітка: це вимагає , щоб ви створити екземпляр підкласу з GenericClass
так що він може правильно пов'язати параметр типу. Інакше він просто поверне тип як T
.
java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized
. Тому я змінив лінію new TypeToken(getClass()) { }
на new TypeToken<T>(getClass()) { }
. Тепер код працює нормально, але Тип все ще 'T'. Дивіться це: 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
, ви повинні зробити цей клас, abstract
щоб неправильне використання не компілювалося.
Звичайно, можна.
Java не використовує інформацію під час виконання з міркувань зворотної сумісності. Але інформація насправді присутня як метадані та до неї можна отримати відображення (але вона все ще не використовується для перевірки типу).
Від офіційного API:
Однак для вашого сценарію я б не використовував роздуми. Я особисто більше схильний використовувати це для рамкового коду. У вашому випадку я просто додав би тип як конструктор парам.
Java-дженерики в основному складають час компіляції, це означає, що інформація про тип втрачається під час виконання.
class GenericCls<T>
{
T t;
}
буде складено до чогось подібного
class GenericCls
{
Object o;
}
Щоб отримати інформацію про тип під час виконання, потрібно додати її як аргумент ctor.
class GenericCls<T>
{
private Class<T> type;
public GenericCls(Class<T> cls)
{
type= cls;
}
Class<T> getType(){return type;}
}
Приклад:
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];
}
}
Я використовував наступний підхід:
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();
}
}
Я не думаю, що ви можете, Java використовує стирання типу під час компіляції, щоб ваш код був сумісний із програмами та бібліотеками, створеними заздалегідь.
З Документів Oracle:
Тип стирання
Generics були представлені на мові Java, щоб забезпечити більш чіткі перевірки типу під час компіляції та підтримати загальне програмування. Для реалізації дженерики компілятор Java застосовує стирання типу для:
Замініть всі параметри типу на загальні типи їх межами або Об'єктом, якщо параметри типу не обмежені. Тому створений байт-код містить лише звичайні класи, інтерфейси та методи. Якщо потрібно, вставте роли типів, щоб зберегти безпеку типу. Створити мостові методи для збереження поліморфізму у розширених родових типах. Стирання типу забезпечує відсутність нових класів для параметризованих типів; отже, генерики не вимагають накладних витрат.
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
Техніка, описана в цій статті Іаном Робертсоном, працює для мене.
Коротше кажучи, швидкий і брудний приклад:
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();
Я думаю, є ще одне елегантне рішення.
Що ви хочете зробити, це (безпечно) "передати" тип параметра загального типу вгору від класу concerete до надкласу.
Якщо ви дозволяєте мислити тип класу як "метадані" в класі, це пропонує метод Java для кодування метаданих під час виконання: анотації.
Спочатку визначте власну примітку за цими рядками:
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
Class entityClass();
}
Потім можна буде додати примітку до свого підкласу.
@EntityAnnotation(entityClass = PassedGenericType.class)
public class Subclass<PassedGenericType> {...}
Тоді ви можете використовувати цей код, щоб отримати тип класу у вашому базовому класі:
import org.springframework.core.annotation.AnnotationUtils;
.
.
.
private Class getGenericParameterType() {
final Class aClass = this.getClass();
EntityAnnotation ne =
AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);
return ne.entityClass();
}
Деякі обмеження такого підходу:
PassedGenericType
) у ДВОХ місцях, а не в одному, який не є DRY.Це моє рішення:
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);
}
}
}
}
Ось робоче рішення !!!
@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 <> ");
}
}
ПРИМІТКИ.
Можна використовувати лише як надклас
1. Потрібно розширити типізованим класом ( Child extends Generic<Integer>
)
АБО
2. Створювати як анонімну реалізацію ( new Generic<Integer>() {};
)
Ось один із способів, яким мені довелося скористатися один-два рази:
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
Разом з
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
Одне просте рішення для цієї кабіни, як нижче
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());
}
}
Щоб завершити деякі відповіді тут, мені довелося отримати параметризований тип MyGenericClass, незалежно від того, наскільки високою є ієрархія, за допомогою рекурсії:
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());
}
}
Ось моя хитрість:
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
є змінною типу, varargs створює масив стирання T
. Див., Наприклад, http://ideone.com/DIPNwd .
На всякий випадок, якщо ви використовуєте зберігання змінної за допомогою загального типу, ви можете легко вирішити цю проблему, додавши метод getClassType таким чином:
public class Constant<T> {
private T value;
@SuppressWarnings("unchecked")
public Class<T> getClassType () {
return ((Class<T>) value.getClass());
}
}
Пізніше я використовую наданий об'єкт класу, щоб перевірити, чи це екземпляр даного класу, як описано нижче:
Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
Constant<Integer> integerConstant = (Constant<Integer>)constant;
Integer value = integerConstant.getValue();
// ...
}
value
це null
? По-друге, що робити, якщо value
це підклас T
? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();
повертається, Integer.class
коли має повернутися Number.class
. Правильніше було б повернутися Class<? extends T>
. Integer.class
є, Class<? extends Number>
але не є Class<Number>
.
Ось моє рішення
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;
}
}
Незалежно від того, скільки рівня має ваша ієрархія класів, це рішення все ще працює, наприклад:
public class FirstLevelChild<T> extends GenericClass<T> {
}
public class SecondLevelChild extends FirstLevelChild<String> {
}
У цьому випадку 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)
Ось моє рішення. Приклади повинні пояснити це. Єдина вимога - підклас повинен встановлювати загальний тип, а не об’єкт.
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;
}
}
Можливо, комусь це стане в нагоді. Ви можете використовувати java.lang.ref.WeakReference; сюди:
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;
N getType(){
return variableToGetTypeFrom.get();
}
}
WeakReference
? Будь ласка, надайте пояснення зі своєю відповіддю, а не лише якийсь код.
SomeClass<MyClass>
ви можете створити екземпляр SomeClass
і викликати getType
цей екземпляр і мати час виконання MyClass
.
WeakReference
? Те, що ви сказали, не відрізняється від більшості інших відповідей.
AtomicReference
, List
, Set
).
Я вважав це простим зрозумілим і легко зрозумілим рішенням
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)
. (Ось чому цей код не працює.)