Cómo obtener información de tipo genérico en Java


Según las diferentes posiciones en las que se utilizan los genéricos, se pueden dividir en: genéricos del lado de la declaración y genéricos del lado del uso.

La información genérica del lado de la declaración se registra en el grupo constante del archivo de clase y se guarda en forma de firma. La información genérica del lado del uso no se guarda.

Genéricos del lado declarativo

Los genéricos del lado de la declaración incluyen:

  1. Clase genérica o declaración de una interfaz genérica
  2. Variables miembro con parámetros genéricos
  3. métodos con parámetros genéricos

genéricos del lado del uso

Los genéricos del lado del uso incluyen:

  1. método de variables locales,
  2. Variables pasadas cuando se llama al método

Obtener métodos relacionados con tipos genéricos

Como se mencionó anteriormente, el tipo genérico en el lado de la declaración se registra en el grupo constante del archivo de clase y se guarda en forma de firma.

Las clases Class, Field y Method de JDK proporcionan una serie de métodos relacionados para obtener tipos genéricos.

1. Métodos genéricos de la clase Class

  • Escriba getGenericSuperclass(): obtenga el tipo de la clase principal

    • Si la clase principal tiene un tipo genérico, el tipo real devuelto es la clase de implementación de la interfaz de tipo parametrizado Clase parametrizada de tipoImpl
    • Si la clase principal no tiene un tipo genérico, el Tipo real devuelto es la clase Clase
  • Type[] getGenericInterfaces(): Obtenga la colección Type de la interfaz principal

    • Si la clase principal tiene un tipo genérico, el tipo real devuelto es la clase de implementación de la interfaz de tipo parametrizado Clase parametrizada de tipoImpl
    • Si la clase principal no tiene un tipo genérico, el Tipo real devuelto es la clase Clase

2. Método genérico de la clase Field

  • Escriba getGenericType(): obtenga el tipo del campo

    • Si el campo tiene un tipo genérico, el tipo real devuelto es la clase de implementación de la interfaz de tipo parametrizado Clase parametrizada de tipoImpl
    • Si el campo no tiene un tipo genérico, el Tipo real devuelto es Clase

3. Método genérico de la clase Method

  • Escriba getGenericReturnType(): obtenga el tipo del valor de retorno del método

    • Si el valor devuelto tiene un tipo genérico, el tipo real devuelto es la clase de implementación de la interfaz de tipo parametrizado Clase parametrizada de tipoImpl
    • Si el valor devuelto no tiene un tipo genérico, el Tipo real devuelto es la clase Clase
  • Type[] getGenericParameterTypes(): Obtener la colección Type de parámetros de método

    • Si el parámetro del método tiene un tipo genérico, el tipo real devuelto es la clase de implementación de la interfaz de tipo parametrizado Clase parametrizada de tipoImpl
    • Si el parámetro del método no tiene un tipo genérico, el Tipo real devuelto es la clase Clase
  • Type[] getGenericExceptionTypes(): Obtiene la colección Type de excepciones declaradas por el método

    • Si el parámetro del método tiene un tipo genérico, el tipo real devuelto es la clase de implementación de la interfaz de tipo parametrizado Clase parametrizada de tipoImpl
    • Si el parámetro del método no tiene un tipo genérico, el Tipo real devuelto es la clase Clase

4. Clase de tipo parametrizado

ParametrizedType es una subinterfaz de Type, que representa un tipo parametrizado y se utiliza para obtener un tipo de parámetro genérico.

El método principal de ParameterizedType:

  • Type[] getActualTypeArguments(): Obtener la colección Type de parámetros de tipo reales

  • Escriba getRawType(): obtenga el tipo de la clase o interfaz que declara este tipo

  • Tipo getOwnerType(): si la clase o interfaz de este tipo se declara como una clase interna, esto devuelve el Tipo de la clase externa de la clase interna (es decir, el propietario de la clase interna)

Obtenga información de tipo genérico en el lado de la declaración

  1. Clase genérica o declaración de una interfaz genérica
  2. Variables miembro con parámetros genéricos
  3. métodos con parámetros genéricos

Ejemplo:

public class MyTest extends TestClass<String> implements TestInterface1<Integer>,TestInterface2<Long> {
    
    

    private List<Integer> list;

    private Map<Integer, String> map;

    public List<String> aa() {
    
    
        return null;
    }

    public void bb(List<Long> list) {
    
    

    }

    public static void main(String[] args) throws Exception {
    
    
        System.out.println("======================================= 泛型类声明的泛型类型 =======================================");
        ParameterizedType parameterizedType = (ParameterizedType)MyTest.class.getGenericSuperclass();
        System.out.println(parameterizedType.getTypeName() + "--------->" + parameterizedType.getActualTypeArguments()[0].getTypeName());

        Type[] types = MyTest.class.getGenericInterfaces();
        for (Type type : types) {
    
    
            ParameterizedType typ = (ParameterizedType)type;
            System.out.println(typ.getTypeName() + "--------->" + typ.getActualTypeArguments()[0].getTypeName());
        }

        System.out.println("======================================= 成员变量中的泛型类型 =======================================");
        ParameterizedType parameterizedType1 = (ParameterizedType)MyTest.class.getDeclaredField("list").getGenericType();
        System.out.println(parameterizedType1.getTypeName() + "--------->" + parameterizedType1.getActualTypeArguments()[0].getTypeName());

        ParameterizedType parameterizedType2 = (ParameterizedType)MyTest.class.getDeclaredField("map").getGenericType();
        System.out.println(parameterizedType2.getTypeName() + "--------->" + parameterizedType2.getActualTypeArguments()[0].getTypeName()+","+parameterizedType2.getActualTypeArguments()[1].getTypeName());

        System.out.println("======================================= 方法参数中的泛型类型 =======================================");
        ParameterizedType parameterizedType3 = (ParameterizedType)MyTest.class.getMethod("aa").getGenericReturnType();
        System.out.println(parameterizedType3.getTypeName() + "--------->" + parameterizedType3.getActualTypeArguments()[0].getTypeName());

        System.out.println("======================================= 方法返回值中的泛型类型 =======================================");
        Type[] types1 = MyTest.class.getMethod("bb", List.class).getGenericParameterTypes();
        for (Type type : types1) {
    
    
            ParameterizedType typ = (ParameterizedType)type;
            System.out.println(typ.getTypeName() + "--------->" + typ.getActualTypeArguments()[0].getTypeName());
        }
    }
}

class TestClass<T> {
    
    

}

interface TestInterface1<T> {
    
    

}

interface TestInterface2<T> {
    
    

}

producción

======================================= 泛型类声明的泛型类型 =======================================
com.joker.test.generic.TestClass<java.lang.String>--------->java.lang.String
com.joker.test.generic.TestInterface1<java.lang.Integer>--------->java.lang.Integer
com.joker.test.generic.TestInterface2<java.lang.Long>--------->java.lang.Long
======================================= 成员变量中的泛型类型 =======================================
java.util.List<java.lang.Integer>--------->java.lang.Integer
java.util.Map<java.lang.Integer, java.lang.String>--------->java.lang.Integer,java.lang.String
======================================= 方法参数中的泛型类型 =======================================
java.util.List<java.lang.String>--------->java.lang.String
======================================= 方法返回值中的泛型类型 =======================================
java.util.List<java.lang.Long>--------->java.lang.Long

Obtenga información de tipo genérico en el lado del uso

Los métodos relacionados con la obtención de tipos genéricos de clases relacionadas mencionados anteriormente son solo para tipos genéricos en el lado de la declaración. Porque el tipo genérico en el lado de la declaración se registra en el grupo constante del archivo de Clase y se guarda en forma de Firma. Entonces, Java proporciona métodos relacionados para obtener esta información.

¿Cómo obtener información genérica del lado del uso? Dado que la información genérica del lado del uso es borrada por el tipo en tiempo de compilación, no hay forma de obtener esta información genérica en tiempo de ejecución.

¿Realmente no hay otra manera?De hecho, todavía hay algunos. El lugar donde se necesita obtener información genérica del lado del usuario es principalmente: la variable genérica que se pasa cuando se llama al método, y el tipo genérico de la variable generalmente se necesita obtener en el método. Por ejemplo, en el escenario del análisis JSON (deserialización), ¿cómo lo lograron?

La principal solución de implementación para obtener información de tipo genérico en el lado del uso es a través de clases internas anónimas.

En este esquema se utilizan la clase abstracta genérica TypeToken<T> en Gson, la clase genérica TypeReference<T> en FastJson, etc.

La implementación de clase interna anónima obtiene el tipo genérico en el lado del uso

Como se mencionó anteriormente, en los genéricos del lado de la declaración, para los genéricos declarados por clases genéricas o interfaces genéricas, la clase Class proporciona getGenericSuperclass() y getGenericInterfaces() para obtener información de tipo genérico específico declarado en sus subclases (clases de implementación).

¿Y qué es una clase interna anónima? Su esencia es un objeto anónimo de subclase que hereda/implementa una determinada clase (interfaz, clase común, clase abstracta).

Las clases internas anónimas implementan el principio de obtener tipos genéricos en el lado del uso:

  1. Defina una clase genérica, y hay un campo de tipo Tipo en la clase genérica, que se usa para guardar el Tipo del tipo genérico
  2. Cree una instancia de subclase de la clase genérica a través de una clase interna anónima (se especifica un tipo genérico específico).En el
    método de construcción para crear una instancia de subclase, la información de tipo genérico se ha obtenido a través de getGenericSuperclass() de la subclase Clase y se ha copiado en el campo del tipo Tipo.
  3. En cualquier momento posterior, siempre que se obtenga la instancia de la subclase, el Tipo del tipo genérico se puede obtener a través de la instancia y se puede obtener la información de la clase genérica en el lado del uso.

Ejemplo sencillo:

Defina la clase genérica TestClass2<T>, que contiene el campo Tipo

public abstract class TestClass2<T> {
    
    

    private final Type type;

    public TestClass2() {
    
    
        Type superClass = getClass().getGenericSuperclass();
        if (!(superClass instanceof ParameterizedType)) {
    
    
            throw new IllegalArgumentException("无泛型类型信息");
        }
        type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() {
    
    
        return type;
    }
}

Prueba obtener tipo genérico

public class Test {
    
    

    public static  <T> T get(TestClass2<T> tTestClass2) throws IllegalAccessException, InstantiationException {
    
    
        Type type = tTestClass2.getType();
        Class clazz = (Class) type;
        return (T)clazz.newInstance();
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
    
    
        String str = get(new TestClass2<String>() {
    
    });
        Date date = get(new TestClass2<Date>() {
    
    });
    }
}

Supongo que te gusta

Origin blog.csdn.net/JokerLJG/article/details/129041452
Recomendado
Clasificación