Как я могу это достичь?
public class GenericClass<T>
{
public Type getMyType()
{
//How do I return the type of T?
}
}
Все, что я пробовал до сих пор, всегда возвращает тип Object
, а не используемый конкретный тип.
Как я могу это достичь?
public class GenericClass<T>
{
public Type getMyType()
{
//How do I return the type of T?
}
}
Все, что я пробовал до сих пор, всегда возвращает тип Object
, а не используемый конкретный тип.
Как упоминалось, это возможно только через отражение при определенных обстоятельствах.
Если вам действительно нужен тип, это обычный (безопасный тип) шаблон обходного пути:
public class GenericClass<T> {
private final Class<T> type;
public GenericClass(Class<T> type) {
this.type = type;
}
public Class<T> getMyType() {
return this.type;
}
}
Я видел что-то вроде этого
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
в hibernate GenericDataAccessObjects Пример
Обобщения не овеществлены во время выполнения. Это означает, что информация отсутствует во время выполнения.
Добавление дженериков в 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 не использует информацию во время выполнения, для соображений обратной совместимости. Но информация фактически присутствует как метаданные и может быть доступна через отражение (но она еще не используется для проверки типов).
Из официального 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;
Я использовал следующий подход:
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();
}
}
Техника, описанная в этой статье Яна Робертсона, работает для меня.
Вкратце быстрый и грязный пример:
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;
}
}
Я не думаю, что вы можете, Java использует стирание стилей при компиляции, чтобы ваш код был совместим с приложениями и библиотеками, которые были созданы с предварительными генериками.
Из документов Oracle:
Тип Erasure
На язык Java были введены обобщения для обеспечения более жесткого типа проверяет время компиляции и поддерживает универсальное программирование. к реализовать generics, компилятор Java применяет стирание типа к:
Заменить все параметры типа в родовых типах на их границы или Объект, если параметры типа не ограничены. Произведенный байт-код, поэтому содержит только обычные классы, интерфейсы и методы. При необходимости вставьте тип приведения, чтобы сохранить безопасность типа. генерировать мостиковые методы для сохранения полиморфизма в расширенных общих типах. Стирание типа гарантирует, что для параметризованных новых классов не создаются типы; следовательно, дженерики не имеют накладных расходов во время выполнения.
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
public abstract class AbstractDao<T>
{
private final Class<T> persistentClass;
public AbstractDao()
{
this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
Это мое решение:
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);
}
}
}
}
Я думаю, что есть еще одно элегантное решение.
Что вы хотите сделать (безопасно) "передать" тип типичного параметра типа от класса 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
) в двух местах, а не один, который не является сухим.Вот рабочее решение!!!
@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>() {};
)
Вы не можете. Если вы добавите переменную-член типа T в класс (вам даже не нужно ее инициализировать), вы можете использовать ее для восстановления типа.
Вот один из способов, который я должен был использовать один или два раза:
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
Наряду с
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
Чтобы выполнить некоторые из ответов здесь, мне пришлось получить ParametrizedType 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 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());
}
}
Вот мой трюк:
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();
}
}
На всякий случай, когда вы используете хранилище переменной с использованием универсального типа, вы можете легко решить эту проблему, добавив метод 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();
// ...
}
Вот мое решение
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
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
Вам не нужен конструктор.
class GenericClass<T> {
public Class<?> getMyType() {
return ((T)this).getClass();
}
}
Это может быть полезно для кого-то. Вы можете использовать java.lang.ref.WeakReference; сюда:
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;
N getType(){
return variableToGetTypeFrom.get();
}
}
Я нашел это простым понятным и легко объяснимым решением
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);
}
}