Como obter informações de tipo genérico em Java


De acordo com as diferentes posições em que os genéricos são usados, eles podem ser divididos em: genéricos do lado da declaração e genéricos do lado do uso.

As informações genéricas do lado da declaração são registradas no pool Constant do arquivo Class e salvas na forma de Signature. As informações genéricas do lado do uso não são salvas.

Genéricos laterais declarativos

Os genéricos do lado da declaração incluem:

  1. Classe genérica ou declaração de uma interface genérica
  2. Variáveis ​​de membro com parâmetros genéricos
  3. métodos com parâmetros genéricos

genéricos de uso

Os genéricos do lado do uso incluem:

  1. variáveis ​​locais do método,
  2. Variáveis ​​passadas quando o método é chamado

Obter métodos relacionados a tipos genéricos

Conforme mencionado acima, o tipo genérico no lado da declaração é registrado no pool Constant do arquivo Class e salvo na forma de Signature.

As classes Class, Field e Method do JDK fornecem uma série de métodos relacionados para obter tipos genéricos.

1. Métodos genéricos da classe Class

  • Digite getGenericSuperclass(): obtenha o tipo da classe pai

    • Se a classe pai tiver um tipo genérico, o Type real retornado é a classe de implementação da classe ParameterizedTypeImpl da interface ParameterizedType
    • Se a classe pai não tiver um tipo genérico, o Type real retornado é a classe Class
  • Type[] getGenericInterfaces(): Obtenha a coleção Type da interface pai

    • Se a classe pai tiver um tipo genérico, o Type real retornado é a classe de implementação da classe ParameterizedTypeImpl da interface ParameterizedType
    • Se a classe pai não tiver um tipo genérico, o Type real retornado é a classe Class

2. Método genérico da classe Field

  • Digite getGenericType(): obtenha o tipo do campo

    • Se o campo tiver um tipo genérico, o Type real retornado é a classe de implementação da classe ParameterizedTypeImpl da interface ParameterizedType
    • Se o campo não tiver um tipo genérico, o Tipo real retornado é Classe

3. Método genérico da classe Method

  • Digite getGenericReturnType(): obtenha o tipo do valor de retorno do método

    • Se o valor de retorno tiver um tipo genérico, o tipo real retornado é a classe de implementação da classe ParameterizedTypeImpl da interface ParameterizedType
    • Se o valor de retorno não tiver um tipo genérico, o Type real retornado é a classe Class
  • Type[] getGenericParameterTypes(): obtém a coleção Type dos parâmetros do método

    • Se o parâmetro do método tiver um tipo genérico, o Type real retornado é a classe de implementação da classe ParameterizedTypeImpl da interface ParameterizedType
    • Se o parâmetro do método não tiver um tipo genérico, o Tipo real retornado é a classe Classe
  • Type[] getGenericExceptionTypes(): Obtenha a coleção Type de exceções declaradas pelo método

    • Se o parâmetro do método tiver um tipo genérico, o Type real retornado é a classe de implementação da classe ParameterizedTypeImpl da interface ParameterizedType
    • Se o parâmetro do método não tiver um tipo genérico, o Tipo real retornado é a classe Classe

4. Classe ParameterizedType

ParameterizedType é uma subinterface de Type, que representa um tipo parametrizado e é usado para obter um tipo de parâmetro genérico.

O método principal de ParameterizedType:

  • Type[] getActualTypeArguments(): obtém a coleção Type dos parâmetros de tipo reais

  • Digite getRawType(): obtenha o tipo da classe ou interface que declara esse tipo

  • Digite getOwnerType(): Se a classe ou interface deste tipo for declarada como uma classe interna, isso retornará o Type da classe externa da classe interna (ou seja, o proprietário da classe interna)

Obtenha informações de tipo genérico no lado da declaração

  1. Classe genérica ou declaração de uma interface genérica
  2. Variáveis ​​de membro com parâmetros genéricos
  3. métodos com parâmetros genéricos

Exemplo:

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> {
    
    

}

saída

======================================= 泛型类声明的泛型类型 =======================================
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

Obtenha informações de tipo genérico no lado do uso

Os métodos relacionados à obtenção de tipos genéricos de classes relacionadas mencionados acima são apenas para tipos genéricos no lado da declaração. Porque o tipo genérico no lado da declaração é registrado no pool Constant do arquivo Class e salvo na forma de Signature. Portanto, Java fornece métodos relacionados para obter essas informações.

Como obter informações genéricas do lado do uso? Como as informações genéricas do lado do uso são apagadas pelo tipo em tempo de compilação, não há como obter essas informações genéricas em tempo de execução.

Não há realmente outra maneira?Na verdade, ainda existem alguns. O lugar onde a informação genérica precisa ser obtida do lado do usuário é principalmente: a variável genérica passada quando o método é chamado, e o tipo genérico da variável geralmente precisa ser obtido no método. Por exemplo, no cenário de análise JSON (desserialização), como eles conseguiram isso.

A principal solução de implementação para obter informações de tipo genérico no lado do uso é por meio de classes internas anônimas.

A classe abstrata genérica TypeToken<T> em Gson, a classe genérica TypeReference<T> em FastJson, etc. são usadas neste esquema.

A implementação de classe interna anônima obtém o tipo genérico no lado do uso

Conforme mencionado acima, nos genéricos do lado da declaração, para os genéricos declarados por classes genéricas ou interfaces genéricas, a classe Class fornece getGenericSuperclass() e getGenericInterfaces() para obter informações específicas de tipo genérico declaradas em suas subclasses (classes de implementação).

E o que é uma classe interna anônima? Sua essência é um objeto anônimo de subclasse que herda/implementa uma determinada classe (interface, classe comum, classe abstrata).

Classes internas anônimas implementam o princípio de obtenção de tipos genéricos no lado do uso:

  1. Defina uma classe genérica, e há um campo do tipo Type na classe genérica, que é usado para salvar o Type do tipo genérico
  2. Crie uma instância de subclasse da classe genérica por meio de uma classe interna anônima (o tipo genérico específico é especificado)
    .
  3. Em qualquer lugar depois, desde que a instância da subclasse seja obtida, o Type do tipo genérico pode ser obtido por meio da instância e as informações da classe genérica no lado do uso podem ser obtidas.

Exemplo simples:

Defina a classe genérica TestClass2<T>, que contém o 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;
    }
}

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

Acho que você gosta

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