Respostas:
Como outros mencionaram, só é possível através da reflexão em determinadas circunstâncias.
Se você realmente precisa do tipo, este é o padrão comum de solução alternativa (com segurança de tipo):
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) {...}
e, em seguida, chamá-lo como tal: GenericClass<String> var = GenericClass.of(String.class)
. Um pouco melhor.
Eu vi algo parecido com isto
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
no exemplo GenericDataAccessObjects hibernar
class A implements Comparable<String>
, o parâmetro de tipo real é String
, mas NÃO PODE dizer que em Set<String> a = new TreeSet<String>()
, o parâmetro de tipo real é String
. De fato, as informações do parâmetro type são "apagadas" após a compilação, conforme explicado em outras respostas.
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
esta resposta.
Class-Mate
o pessoal de Jackson. Eu escrevi uma essência aqui gist.github.com/yunspace/930d4d40a787a1f6a7d1
(Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()
para obter argumentos de tipo reais.
Os genéricos não são reificados em tempo de execução. Isso significa que as informações não estão presentes no tempo de execução.
Adicionar genéricos ao Java e manter a compatibilidade com versões anteriores foi um tour de force (você pode ver o documento seminal sobre o assunto: Tornando o futuro seguro para o passado: adicionando genéricos à linguagem de programação Java ).
Há uma rica literatura sobre o assunto, e algumas pessoas estão insatisfeitas com o estado atual, outros dizem que, na verdade, é uma atração e não há necessidade real disso. Você pode ler os dois links, eu os achei bastante interessantes.
Use goiaba.
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
}
}
Há algum tempo, publiquei alguns exemplos completos, incluindo classes abstratas e subclasses aqui .
Nota: isso requer que você instancia uma subclasse de GenericClass
para que possa ligar o parâmetro type corretamente. Caso contrário, ele retornará o tipo como T
.
java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized
. Então eu mudei a linha new TypeToken(getClass()) { }
para new TypeToken<T>(getClass()) { }
. Agora, o código funciona bem, mas o Type ainda é 'T'. Veja isto: 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
, você deve fazer essa classe abstract
para que o uso errado não seja compilado.
Certamente você pode.
Java não usa as informações no tempo de execução, por motivos de compatibilidade com versões anteriores. Mas as informações estão realmente presentes como metadados e podem ser acessadas via reflexão (mas ainda não são usadas para verificação de tipo).
A partir da API oficial:
No entanto , para o seu cenário, eu não usaria a reflexão. Pessoalmente, estou mais inclinado a usar isso como código de estrutura. No seu caso, eu apenas adicionaria o tipo como um parâmetro do construtor.
Os genéricos Java geralmente são em tempo de compilação, isso significa que as informações do tipo são perdidas no tempo de execução.
class GenericCls<T>
{
T t;
}
será compilado para algo como
class GenericCls
{
Object o;
}
Para obter as informações de tipo em tempo de execução, você deve adicioná-las como argumento do ctor.
class GenericCls<T>
{
private Class<T> type;
public GenericCls(Class<T> cls)
{
type= cls;
}
Class<T> getType(){return type;}
}
Exemplo:
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];
}
}
Eu usei a seguinte abordagem:
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();
}
}
Eu não acho que você pode, o Java usa apagamento de tipo ao compilar para que seu código seja compatível com aplicativos e bibliotecas que foram criadas pré-genéricas.
No Oracle Docs:
Tipo Erasure
Os genéricos foram introduzidos na linguagem Java para fornecer verificações mais restritas de tipo em tempo de compilação e para oferecer suporte à programação genérica. Para implementar genéricos, o compilador Java aplica o apagamento de tipo a:
Substitua todos os parâmetros de tipo em tipos genéricos por seus limites ou Objeto se os parâmetros de tipo não tiverem limites. O bytecode produzido, portanto, contém apenas classes, interfaces e métodos comuns. Insira moldes do tipo, se necessário, para preservar a segurança do tipo. Gere métodos de ponte para preservar o polimorfismo em tipos genéricos estendidos. O apagamento de tipo garante que nenhuma nova classe seja criada para tipos parametrizados; consequentemente, os genéricos não sofrem sobrecarga de tempo de execução.
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
Técnica descrita neste artigo por Ian Robertson funciona para mim.
Em resumo, exemplo rápido e sujo:
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();
Eu acho que há outra solução elegante.
O que você deseja fazer é (com segurança) "passar" o tipo do parâmetro de tipo genérico da classe concerete para a superclasse.
Se você se permitir pensar no tipo de classe como "metadados" na classe, isso sugere o método Java para codificar metadados no tempo de execução: anotações.
Primeiro defina uma anotação personalizada ao longo destas linhas:
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
Class entityClass();
}
Você pode adicionar a anotação à sua subclasse.
@EntityAnnotation(entityClass = PassedGenericType.class)
public class Subclass<PassedGenericType> {...}
Então você pode usar este código para obter o tipo de classe na sua classe base:
import org.springframework.core.annotation.AnnotationUtils;
.
.
.
private Class getGenericParameterType() {
final Class aClass = this.getClass();
EntityAnnotation ne =
AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);
return ne.entityClass();
}
Algumas limitações dessa abordagem são:
PassedGenericType
) em DOIS lugares, em vez de um que não seja SECO.Esta é a minha solução:
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);
}
}
}
}
Aqui está a solução de trabalho !!!
@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 <> ");
}
}
OBSERVAÇÕES:
Pode ser usado apenas como superclasse
1. Deve ser estendido com a classe digitada ( Child extends Generic<Integer>
)
OU
2. Deve ser criado como implementação anônima ( new Generic<Integer>() {};
)
Você não pode. Se você adicionar uma variável de membro do tipo T à classe (você nem precisa inicializá-la), poderá usá-la para recuperar o tipo.
Aqui está uma maneira, que eu tive que usar uma ou duas vezes:
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
Junto com
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
Uma solução simples para esta cabine é como abaixo
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());
}
}
Para concluir algumas das respostas aqui, tive que obter o ParametrizedType de MyGenericClass, não importa quão alta seja a hierarquia, com a ajuda da recursão:
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());
}
}
Aqui está o meu truque:
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
é uma variável de tipo. No caso de T
uma variável de tipo, as varargs criam uma matriz de apagamento de T
. Veja, por exemplo, http://ideone.com/DIPNwd .
No caso de você usar armazenar uma variável usando o tipo genérico, você pode resolver facilmente esse problema adicionando um método getClassType da seguinte maneira:
public class Constant<T> {
private T value;
@SuppressWarnings("unchecked")
public Class<T> getClassType () {
return ((Class<T>) value.getClass());
}
}
Uso o objeto de classe fornecido posteriormente para verificar se é uma instância de uma determinada classe, da seguinte maneira:
Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
Constant<Integer> integerConstant = (Constant<Integer>)constant;
Integer value = integerConstant.getValue();
// ...
}
value
for null
? Em segundo lugar, e se value
for uma subclasse de T
? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();
retorna Integer.class
quando deveria retornar Number.class
. Seria mais correto retornar Class<? extends T>
. Integer.class
é um Class<? extends Number>
mas não um Class<Number>
.
Aqui está a minha solução
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;
}
}
Não importa quantos níveis sua hierarquia de classes tenha, esta solução ainda funciona, por exemplo:
public class FirstLevelChild<T> extends GenericClass<T> {
}
public class SecondLevelChild extends FirstLevelChild<String> {
}
Nesse caso, 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)
Aqui está a minha solução. Os exemplos devem explicar isso. O único requisito é que uma subclasse deve definir o tipo genérico, não um objeto.
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;
}
}
Pode ser útil para alguém. Você pode usar java.lang.ref.WeakReference; por aqui:
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;
N getType(){
return variableToGetTypeFrom.get();
}
}
WeakReference
? Forneça alguma explicação com sua resposta, não apenas algum código.
SomeClass<MyClass>
pode instanciar SomeClass
e chamar getType
nessa instância e ter o tempo de execução atual MyClass
.
WeakReference
? O que você disse não é diferente da maioria das outras respostas.
AtomicReference
, List
, Set
).
Achei que era uma solução simples, compreensível e facilmente explicável
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)
. (É por isso que esse código não funciona).