Uzyskaj ogólny typ klasy w czasie wykonywania


508

Jak mogę to osiągnąć?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Wszystko, czego do tej pory próbowałem, zawsze zwraca typ, Objecta nie określony typ.


3
@SamuelVidal To nie jest .NET, to jest Java.
— Frontear

Odpowiedzi:


317

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;
     }
}

58
Podoba mi się ta odpowiedź, ale tworzenie instancji jest trochę kłopotliwe: GenericClass <AnotherClass> g = nowa GenericClass <AnotherClass> (AnotherClass.class);
— Eliseo Ocampos

1
To jeszcze bardziej szczegółowe, jeśli zastosujesz podejście dao / factory / manager. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
— djmj

2
To prawda, ale nie działa we wszystkich przypadkach, takich jak bezstanowe zdalne ziarna, które są tworzone przez kontener / odbicie.
— djmj

6
Jako kontynuacja mojego poprzedniego komentarza - po wielu cierpieniach bawiących się refleksją, skończyłem na tej odpowiedzi.
— Tomáš Zato - Przywróć Monikę

18
Można obejść zbędne odniesienia, podając ogólną statyczną metodę fabryczną. Coś podobnego 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.
— Joeri Hendrickx

262

Widziałem coś takiego

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

w hibernacji GenericDataAccessObjects Przykład


84
Ta technika działa tam, gdzie parametr typu jest zdefiniowany w bezpośredniej nadklasie, ale kończy się niepowodzeniem, jeśli parametr typu jest zdefiniowany gdzie indziej w hierarchii typów. Do obsługi bardziej skomplikowanych przypadków można użyć czegoś takiego jak TypeTools . Dokumenty zawierają przykład bardziej wyrafinowanego generycznego DAO.
— Jonathan

25
To zwraca tylko rzeczywiste parametry typu stosowane, gdy CLASS narzędzia / rozciąga się coś, co ma ogólnych deklaracji, że nie zwraca rzeczywiste parametry rodzaju stosowanych kiedy ZDARZEŃ jest tworzony. Innymi słowy, MOŻE powiedzieć, że w 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.
— Siu Ching Pong -Asuka Kenji-

32
Dostaję java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypetę odpowiedź.
— Tomáš Zato - Przywróć Monikę

4
Takie podejście można również osiągnąć, korzystając Class-Matez pomocy ludzi z Jacksonów. Napisałem tutaj gist.github.com/yunspace/930d4d40a787a1f6a7d1
— yunspace

5
@ TomášZato Wywołanie po prostu powyższego kodu zwróciło dla mnie ten sam wyjątek. Wiem, że jest trochę za późno, ale w moim przypadku musiałem zadzwonić, (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()aby uzyskać argumenty typu rzeczywistego.
— AndrewMcCoist,

98

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.


179
Oczywiście jesteśmy niezadowoleni. NET ma znacznie lepszy ogólny mechanizm obsługi
— Pacerier

6
@Pacerier: ale sama wersja generics nie wprowadziłaby Java na poziom .NET. Typy wartości wyspecjalizowany kod dla nich jest co najmniej tak samo ważny dla tego, dlaczego .NET jest lepszy w obszarze ogólnych.
— Joachim Sauer,

@JoachimSauer, tak typy wartości. Zawsze chciałem tych w java. Btw, co masz na myśli przez specjalistyczny kod?
— Pacerier,

3
@ spaaarky21 Nie, ogólne parametry typu są usuwane podczas kompilacji (tak zwane „kasowanie”, możesz google go). Sztuczka w odpowiedzi FrVaBe działa tylko wtedy, gdy parametry typu nadklasy są znane statycznie (patrz pierwszy komentarz Johnathna)
— ewernli

1
Usuwanie typu Java jest wadą historycznego projektu; napisano więcej kodu, aby go obejść, niż napisano, aby go zaimplementować.
— Abhijit Sarkar

68

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.


3
Zauważ, że tworzę pustą anonimową podklasę (patrz dwa nawiasy klamrowe na końcu). Używa refleksji do walki z usuwaniem typu Java. Możesz dowiedzieć się więcej tutaj: code.google.com/p/guava-libraries/wiki/ReflectionExplained
— Cody A. Ray

3
@ CodyA.Ray Twój kod wyrzuca a 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
— Manu Manjunath

3
@Dominik Zobacz zaktualizowany przykład, który możesz skopiować i wkleić, aby sprawdzić się. Dodałem także uwagę wyjaśniającą, że musisz utworzyć instancję podklasy (jak pokazano). Zgodnie z ogólną wskazówką dotyczącą etykiety, przeczytaj wszystkie powiązane artykuły i powiązane javadoki, zanim oskarżysz plakat o „pobożne życzenia”. Użyłem podobnego kodu produkcyjnego wiele razy. Pomocnicy Guava, których pokazuję, są przeznaczeni do tego konkretnego przypadku użycia, a ich javadoki pokazują prawie dokładną odpowiedź na to pytanie. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
— Cody A. Ray

1
Ta odpowiedź działa świetnie w Javie 8 - mam interfejs i klasę fabryczną do emitowania różnych typów i chciałem, aby użytkownicy mogli łatwo określić typ. W moim interfejsie dodałem domyślną metodę: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
— Richard Sand,

2
@ CodyA.Ray Ponieważ działa to tylko z podklasami GenericClass, powinieneś sprawić, aby ta klasa abstractnie była użyta do niewłaściwego użycia.
— Martin

37

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:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

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.


10
getActualTypeArguments zwraca tylko argumenty typu dla klasy bezpośredniej. Jeśli masz złożoną hierarchię typów, w której T można sparametryzować w dowolnym miejscu w hierarchii, musisz wykonać trochę pracy, aby dowiedzieć się, co to jest. Jest to mniej więcej to, co robi TypeTools .
— Jonathan

36

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;

9
private final Class<T> type;
— naXa,

Jak mogę z niego utworzyć typ tablicy:Type t = //String[]
— Paweł Cioch

@PawelCioch java.lang.reflect.Array.newInstance (typ elementu, długość); Mam nadzieję, że to pomoże (javadoc można znaleźć tutaj docs.oracle.com/javase/8/docs/api/java/lang/reflect/… )
— josefx

@PawelCioch pominął metodę .getClass (), aby uzyskać typ z utworzonej tablicy. Wydaje się, że nie ma bezpośredniego sposobu uzyskania klasy tablicy. Większość kolekcji Java używa zamiast tego Object [].
— josefx

23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}

3
Popieram tę odpowiedź, ponieważ jest to rozwiązanie, które działa na zadawane pytanie. Jednak dla tych, którzy chcą nawigować w górę w hierarchii klas, jak ja z więcej niż jedną klasą Ogólną, to nie zadziała. Ponieważ otrzymasz rzeczywistą klasę java.lang.object.
— KMC

3
Należy pamiętać, że to rozwiązanie działa TYLKO, jeśli klasą zawierającą typ ogólny jest ABSTRAKT
— BabaNew

Nie działał z moją klasą abstrakcyjną w Javie 11
— JRA_TLL

@JRA_TLL najwyraźniej zrobiłeś coś złego. Właśnie użyłem go z Javą 12 i działa jak urok.
— Googie

Jeśli chcesz nawigować w górę w hierarchii widoku, możesz rzutować ogólnąSuperclass do klasy <*> i uzyskać ogólnąSuperclass. Najlepiej w pętli.
— fupduck

21

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();
    }
}

9
Mam „wyjątek w głównym wątku” java.lang.ClassCastException: java.lang.Class nie można rzutować na java.lang.reflect.ParameterizedType ”z tym kodem próbki
— yuyang

16

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


Jup, to niemożliwe. Aby to działało, Java wymaga poprawionych rodzajów ogólnych.
— Henning

15

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;
    }
  }

3
W której konkretnej linii w tym kodzie znajdują się rzeczywiste parametry odczytu w czasie wykonywania?
— Ivan Matavulj

tutaj? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
— Ondrej Bozek

9

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:

  1. Typ ogólny ( PassedGenericType) podajesz w DWÓCH miejscach, a nie w jednym, który nie jest osuszony.
  2. Jest to możliwe tylko wtedy, gdy można zmodyfikować konkretne podklasy.

Tak, nie jest w stanie DRY, jednak jest czystszy niż sugerowane powyżej podejście do rozszerzenia. Lubię to. dzięki
— agodinhost

8

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);
      }
    }
  }
}

10
To nie jest odpowiedź na to pytanie.
— Adam Arold,

1
Mój kod nie jest dokładnym rozwiązaniem pytania. Zwraca ogólne parametry typu klasy, ale nie rzeczywisty typ T. Ale może to być pomocne dla innych, którzy natkną się na pytanie i szukają mojego rozwiązania.
— Matthias M

1
getClass (). getGenericSuperclass () osiągnie ten sam efekt.
— Gorky

5

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>() {};)


4

Nie możesz Jeśli dodasz zmienną składową typu T do klasy (nawet nie musisz jej inicjować), możesz użyć jej do odzyskania typu.


2
Ups, ok. Ty nie musiał zainicjować go z konstruktora.
— andrewmu

4

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;
    }
}

4
To technicznie działa, ale to nie rozwiązuje ogólnego przypadku i myślę, że o to właśnie chodzi w oryginalnym plakacie.
— ggb667

To nie zasługuje na głosowanie tak jak jest - oryginalny plakat nie był jednoznaczny. Ta odpowiedź oferuje wzorzec projektowy, który działa i jest łatwy do wdrożenia, pod warunkiem, że nadaje się abstrakcyjność klasy ogólnej.
— VirtualMichael,

4

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());
    }
}

3

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());
    }
}

1

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();
    }

}

1
Uwaga: to nie działa, gdy Tjest zmienną typu. W przypadku, gdy Tjest to zmienna typu, varargs tworzy tablicę usuwania T. Zobacz np . Http://ideone.com/DIPNwd .
— Radiodef

1
Zwraca „Obiekt”
— yahya

Być może próbujesz odpowiedzieć na inne pytanie 🤔
— Khan

1

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();
    // ...
}

2
Jest to niestety problematyczne. Po pierwsze, co jeśli 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>.
— Radiodef

1

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


To nie zwraca typu T. Zwraca T, a nie java.lang.String poza tym, że kod nie
— ukrywa

Oto próbka online, którą wykonałem. Kliknij skompiluj i uruchom, a następnie uzyskasz wynik. tutorialspoint.com/…
— Lin Yu Cheng

Działa dla mnie - kiedy WildFly Weld CDI złamał alternatywną metodę.
— Andre

MamException 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)
— yuyang

0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}

0

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;
    }

}

-1

Zrobiłem to samo, co @Moesio Above, ale w Kotlin można to zrobić w ten sposób:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}

-4

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();
  }
}

Jak ta klasa ma być używana? Dlaczego WeakReference? Podaj wyjaśnienie swojej odpowiedzi, a nie tylko kod.
— Abhijit Sarkar

Więc jeśli masz SomeClass<MyClass> , możesz utworzyć instancję SomeClassi zadzwonić getTypedo tej instancji oraz mieć czas działania MyClass.
— TheLetch,

Jasne, ale dlaczego WeakReference? To, co powiedziałeś, nie różni się od większości innych odpowiedzi.
— Abhijit Sarkar

Po pierwsze, moje podejście jest krótsze (mniej kodu), po drugie słabe referencje nie uniemożliwiają sfinalizowania ich odniesień, i o ile wiem, nie używa refleksji, więc jest szybkie
— TheLetch

To nie dostać typ czegokolwiek, to zwraca obiekt tego typu, który FYI, można zrobić z dosłownie każdego rodzaju owijki ( AtomicReference, List, Set).
— Frontear

-5

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);
    }
}

Wyjaśnić (T...t). (Dlatego ten kod nie działa.)
— Christopher Schneider
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.