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.