Odpowiedzi:
Jak wspomniano inni, jest to możliwe tylko poprzez refleksję w określonych okolicznościach.
Jeśli naprawdę potrzebujesz tego typu, jest to zwykły (bezpieczny dla typu) sposób obejścia:
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) {...}, a następnie wywołać go jako takie: GenericClass<String> var = GenericClass.of(String.class). Trochę ładniej.
Widziałem coś takiego
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
w hibernacji GenericDataAccessObjects Przykład
class A implements Comparable<String>parametrze typu rzeczywistego jest String, ale NIE MOŻE powiedzieć, że w Set<String> a = new TreeSet<String>()parametrze typu rzeczywistego jest String. W rzeczywistości informacje o parametrach typu są „usuwane” po kompilacji, jak wyjaśniono w innych odpowiedziach.
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypetę odpowiedź.
Class-Matez pomocy ludzi z Jacksonów. Napisałem tutaj gist.github.com/yunspace/930d4d40a787a1f6a7d1
(Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()aby uzyskać argumenty typu rzeczywistego.
Generyczne nie są weryfikowane w czasie wykonywania. Oznacza to, że informacje nie są obecne w czasie wykonywania.
Dodanie generycznych składników do Javy przy jednoczesnym zachowaniu kompatybilności wstecznej było tour-de-force (możesz przeczytać o tym artykuł na ten temat: Zapewnienie bezpieczeństwa przeszłości: dodanie ogólności do języka programowania Java ).
Istnieje bogata literatura na ten temat, a niektórzy ludzie są niezadowoleni z obecnego stanu, niektórzy twierdzą, że tak naprawdę jest to przynęta i nie ma takiej potrzeby. Możesz przeczytać oba linki, uważam je za dość interesujące.
Użyj Guawy.
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
}
}
Jakiś czas temu napisałem kilka przykładów pełnym fledge tym abstrakcyjnych klas i podklas tutaj .
Uwaga: to wymaga instancji podklasy z GenericClasswięc może wiązać parametru typu poprawnie. W przeciwnym razie po prostu zwróci typ jako T.
java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. Więc zmieniłem linię new TypeToken(getClass()) { }na new TypeToken<T>(getClass()) { }. Teraz kod działa poprawnie, ale Type to wciąż „T”. Zobacz: 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, powinieneś sprawić, aby ta klasa abstractnie była użyta do niewłaściwego użycia.
Oczywiście że możesz.
Java nie korzysta z tych informacji w czasie wykonywania, ze względu na kompatybilność wsteczną. Ale informacje są faktycznie obecne jako metadane i można uzyskać do nich dostęp poprzez odbicie (ale nadal nie są wykorzystywane do sprawdzania typu).
Z oficjalnego API:
Jednak w twoim scenariuszu nie użyłbym refleksji. Osobiście jestem bardziej skłonny do używania go do kodu frameworka. W twoim przypadku po prostu dodałbym ten typ jako parametr konstruktora.
Ogólnie rzecz biorąc, Java jest czasem kompilacji, co oznacza, że informacje o typie są tracone w czasie wykonywania.
class GenericCls<T>
{
T t;
}
zostanie skompilowany do czegoś podobnego
class GenericCls
{
Object o;
}
Aby uzyskać informacje o typie w czasie wykonywania, musisz dodać je jako argument ctor.
class GenericCls<T>
{
private Class<T> type;
public GenericCls(Class<T> cls)
{
type= cls;
}
Class<T> getType(){return type;}
}
Przykład:
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];
}
}
Zastosowałem następujące podejście:
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();
}
}
Nie sądzę, abyś mógł, podczas kompilacji Java używa wymazywania typu, aby twój kod był zgodny z aplikacjami i bibliotekami, które zostały utworzone przed generycznymi.
Z dokumentów Oracle:
Wpisz Usuń
W języku Java wprowadzono generyczne, aby zapewnić dokładniejsze kontrole typów w czasie kompilacji i aby wspierać ogólne programowanie. Aby zaimplementować ogólne, kompilator Java stosuje usuwanie typu:
Zastąp wszystkie parametry typu w typach ogólnych ich granicami lub Obiektem, jeśli parametry typu są nieograniczone. Dlatego wygenerowany bytecode zawiera tylko zwykłe klasy, interfejsy i metody. W razie potrzeby wstaw odlewy typu, aby zachować bezpieczeństwo typu. Generuj metody pomostowe, aby zachować polimorfizm w rozszerzonych typach ogólnych. Kasowanie typów zapewnia, że nie są tworzone nowe klasy dla sparametryzowanych typów; w związku z tym, generyczne nie wiążą się z narzutem czasu wykonywania.
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
Technika opisana w tym artykule przez Iana Robertsona działa dla mnie.
W skrócie szybki i brudny przykład:
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();
Myślę, że istnieje inne eleganckie rozwiązanie.
To, co chcesz zrobić, to (bezpiecznie) „przekazać” typ parametru typu ogólnego z klasy ukrytej do nadklasy.
Jeśli pozwolisz sobie myśleć o typie klasy jako o „metadanych” w klasie, sugeruje to metodę Java do kodowania metadanych w czasie wykonywania: adnotacje.
Najpierw zdefiniuj niestandardową adnotację według następujących linii:
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
Class entityClass();
}
Następnie możesz dodać adnotację do swojej podklasy.
@EntityAnnotation(entityClass = PassedGenericType.class)
public class Subclass<PassedGenericType> {...}
Następnie możesz użyć tego kodu, aby uzyskać typ klasy w klasie podstawowej:
import org.springframework.core.annotation.AnnotationUtils;
.
.
.
private Class getGenericParameterType() {
final Class aClass = this.getClass();
EntityAnnotation ne =
AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);
return ne.entityClass();
}
Niektóre ograniczenia tego podejścia to:
PassedGenericType) podajesz w DWÓCH miejscach, a nie w jednym, który nie jest osuszony.To jest moje rozwiązanie:
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);
}
}
}
}
Oto działające rozwiązanie !!!
@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 <> ");
}
}
UWAGI:
Może być używany tylko jako nadklasa
1. Musi zostać rozszerzony o typ maszynowy ( Child extends Generic<Integer>)
LUB
2. Musi zostać utworzony jako anonimowa implementacja ( new Generic<Integer>() {};)
Oto jeden ze sposobów, z których musiałem skorzystać raz lub dwa:
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
Wraz z
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
Jedno proste rozwiązanie dla tej kabiny jest jak poniżej
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());
}
}
Aby wypełnić niektóre odpowiedzi tutaj, musiałem uzyskać ParametrizedType MyGenericClass, bez względu na to, jak wysoka jest hierarchia, za pomocą rekurencji:
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());
}
}
Oto moja sztuczka:
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();
}
}
Tjest zmienną typu. W przypadku, gdy Tjest to zmienna typu, varargs tworzy tablicę usuwania T. Zobacz np . Http://ideone.com/DIPNwd .
Na wypadek, gdybyś użył funkcji przechowywania zmiennej przy użyciu typu ogólnego, możesz łatwo rozwiązać ten problem, dodając metodę getClassType w następujący sposób:
public class Constant<T> {
private T value;
@SuppressWarnings("unchecked")
public Class<T> getClassType () {
return ((Class<T>) value.getClass());
}
}
Używam podanego obiektu klasy później, aby sprawdzić, czy jest to instancja danej klasy, w następujący sposób:
Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
Constant<Integer> integerConstant = (Constant<Integer>)constant;
Integer value = integerConstant.getValue();
// ...
}
valuejest null? Po drugie, co jeśli valuejest podklasą T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();zwraca, Integer.classkiedy powinien wrócić Number.class. Lepiej byłoby wrócić Class<? extends T>. Integer.class jest, Class<? extends Number> ale nie jest Class<Number>.
Oto moje rozwiązanie
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;
}
}
Niezależnie od tego, ile poziomów ma Twoja hierarchia klas, to rozwiązanie nadal działa, na przykład:
public class FirstLevelChild<T> extends GenericClass<T> {
}
public class SecondLevelChild extends FirstLevelChild<String> {
}
W takim przypadku 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)
Oto moje rozwiązanie. Przykłady powinny to wyjaśniać. Jedynym wymaganiem jest to, że podklasa musi ustawić typ ogólny, a nie obiekt.
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;
}
}
Może to być przydatne dla kogoś. Możesz użyć java.lang.ref.WeakReference; tą drogą:
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;
N getType(){
return variableToGetTypeFrom.get();
}
}
WeakReference? Podaj wyjaśnienie swojej odpowiedzi, a nie tylko kod.
SomeClass<MyClass> , możesz utworzyć instancję SomeClassi zadzwonić getTypedo tej instancji oraz mieć czas działania MyClass.
WeakReference? To, co powiedziałeś, nie różni się od większości innych odpowiedzi.
AtomicReference, List, Set).
Uznałem, że jest to proste, zrozumiałe i łatwe do wyjaśnienia rozwiązanie
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). (Dlatego ten kod nie działa.)