Java反射 - 泛型

使用Java泛型通常分为两种不同的情况:

  1. 声明一个类/接口是可参数化的。
  2. 使用可参数化的类。
    当你写一个类或接口时,你可以指定它应该是可参数化的。 java.util.List接口就是这种情况。 而不是创建一个Object列表,你可以参数化java.util.List来创建一个String String列表,如下所示:
List<String> myList = new ArrayList<String>();

当通过反射在运行时检查可参数化类型本身时,如java.util.List,无法知道类型已经被参数化。对象本身不知道它被参数化了什么。

但是,该对象的引用知道包含它所引用的泛型的类型。也就是说,如果它不是一个局部变量。如果对象被对象中的字段引用,则可以通过反射来查看Field声明,以获取有关由该字段声明的泛型类型的信息。

如果对象被方法中的参数引用,也是一样的。通过该方法的Parameter对象(一个Java反射对象),您可以看到声明了哪个泛型类型的参数。

最后,您还可以查看方法的返回类型,以查看它声明的泛型类型。同样,你不能从返回的实际对象中看到它。您需要通过反射来查看方法声明,以查看它声明的返回类型(包括泛型)。

综上所述:只能从引用的声明(字段,参数,返回类型)中看到这些引用引用的对象所具有的通用类型。你不能从对象本身看到它。

以下各节将仔细研究这些情况。

泛型方法返回类型

如果已经获得java.lang.reflect.Method对象,则可以获取有关其通用返回类型的信息。 您可以阅读如何获取文本“Java泛型:方法”中的Method对象。 下面是一个带有参数化返回类型的方法的示例类:

public class MyClass {

  protected List<String> stringList = ...;

  public List<String> getStringList(){
    return this.stringList;
  }
}

在这个类中,可以获得getStringList()方法的通用返回类型。 换句话说,可以检测到getStringList()返回一个List <String>而不仅仅是一个List。

Method method = MyClass.class.getMethod("getStringList", null);

Type returnType = method.getGenericReturnType();

if(returnType instanceof ParameterizedType){
    ParameterizedType type = (ParameterizedType) returnType;
    Type[] typeArguments = type.getActualTypeArguments();
    for(Type typeArgument : typeArguments){
        Class typeArgClass = (Class) typeArgument;
        System.out.println("typeArgClass = " + typeArgClass);
    }
}

这段代码将打印出“typeArgClass = java.lang.String”文本。 Type []数组typeArguments数组将包含一个项目 - 一个表示java.lang.String类的Class实例。 类实现了Type接口。

泛型方法参数类型

您也可以通过Java Reflection在运行时访问通用类型的参数类型。 下面是一个带有参数化List作为参数的方法的示例类:

public class MyClass {
  protected List<String> stringList = ...;

  public void setStringList(List<String> list){
    this.stringList = list;
  }
}
method = Myclass.class.getMethod("setStringList", List.class);

Type[] genericParameterTypes = method.getGenericParameterTypes();

for(Type genericParameterType : genericParameterTypes){
    if(genericParameterType instanceof ParameterizedType){
        ParameterizedType aType = (ParameterizedType) genericParameterType;
        Type[] parameterArgTypes = aType.getActualTypeArguments();
        for(Type parameterArgType : parameterArgTypes){
            Class parameterArgClass = (Class) parameterArgType;
            System.out.println("parameterArgClass = " + parameterArgClass);
        }
    }
}

这段代码将打印出“parameterArgType = java.lang.String”文本。 Type []数组的parameterArgTypes数组将包含一个项 - 一个表示类java.lang.String的Class实例。 类实现了Type接口。

通用字段类型

也可以访问通用类型的公共字段。 字段是类成员变量 - 静态或实例变量。 您可以阅读关于在文本“Java Generics:Fields”中获取Field对象的信息。 这是前面的例子,带有一个名为stringList的实例字段。

public class MyClass {
  public List<String> stringList = ...;
}
Field field = MyClass.class.getField("stringList");

Type genericFieldType = field.getGenericType();

if(genericFieldType instanceof ParameterizedType){
    ParameterizedType aType = (ParameterizedType) genericFieldType;
    Type[] fieldArgTypes = aType.getActualTypeArguments();
    for(Type fieldArgType : fieldArgTypes){
        Class fieldArgClass = (Class) fieldArgType;
        System.out.println("fieldArgClass = " + fieldArgClass);
    }
}

这段代码将打印出“fieldArgClass = java.lang.String”文本。 Type []数组fieldArgTypes数组将包含一个项目 - 一个表示类java.lang.String的Class实例。 类实现了Type接口.

实战

package com.reflection.detail;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 反射泛型
 * Created by Fant.J.
 * 2018/2/7 16:31
 */
public class Reflection_Generics {
    public static void main(String[] args) throws NoSuchMethodException {

        Class aClass = People.class;
        Method getStringList = aClass.getMethod("getStringList", null);
        //获取方法的返回值类型
        Type returnType = getStringList.getGenericReturnType();
        System.out.println(returnType);

        if(returnType instanceof ParameterizedType){
            ParameterizedType type = (ParameterizedType) returnType;
            Type[] typeArguments = type.getActualTypeArguments();
            for(Type typeArgument : typeArguments){
                Class typeArgClass = (Class) typeArgument;
                System.out.println("typeArgClass = " + typeArgClass);
            }
        }

    }
}

java.util.List<java.lang.String>
typeArgClass = class java.lang.String
### Java 全面知识点与基础教程 #### 什么是Java(Generics)是一种提供编译时类检查的机制,允许在类、接口和方法中定义、传递和操作各种类的对象,而无需明确指定具体类。这种设计可以增强代码的可读性、可维护性,并减少类转换错误[^1]。 --- #### 的关键概念 ##### 1. **类** 类是指可以在类声明时使用一个或多个类参数的类。这些类参数可以用作字段、方法返回值或方法参数的占位符。 - **基本格式**: ```java public class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } } ``` - **注意事项**: - 类的实际类是在创建对象时指定的。 - 编译器会在运行时执行类擦除,因此无法在类中直接使用 `new T()` 创建实例[^3]。 --- ##### 2. **接口** 类似于类,接口也可以接受类参数。 - **基本格式**: ```java public interface Generator<T> { T next(); } ``` - **注意事项**: - 实现接口的类可以选择固定某个类或者保留特性。 ```java // 固定类 public class StringGenerator implements Generator<String> { @Override public String next() { return "Hello"; } } // 保持 public class GenericGenerator<T> implements Generator<T> { @Override public T next() { return null; // 或者其他逻辑 } } ``` --- ##### 3. **方法** 方法是指在其签名中包含自己的类参数的方法。即使该方法属于非类,它仍然可以独立于类的类参数工作。 - **基本格式**: ```java public <T> void method(T param) {} ``` - **示例**: ```java public class Util { public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } } class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } ``` --- ##### 4. **通配符** 通配符用于表示未知类,增强了使用的灵活性。 - **协变 (`<? extends T>`)**:只允许访问子类的数据。 ```java List<? extends Number> list = new ArrayList<>(); Number num = list.get(0); // 可以获取数据 // list.add(new Integer(1)); 不支持添加任何元素 ``` - **逆变 (`<? super T>`)**:只允许访问父类的数据。 ```java List<? super Integer> list = new ArrayList<>(); list.add(new Integer(1)); // 支持添加Integer及其子类 Object obj = list.get(0); // 获取到的是Object类 ``` - **无界通配符 (`<?>`)**:表示任意类。 ```java List<?> list = new ArrayList<>(); Object obj = list.get(0); ``` --- ##### 5. **类擦除** 类擦除是 Java 中的一个重要概念,指的是在编译阶段会移除所有的信息,在字节码层面不再存在具体的类约束。 - **影响**: - 运行时无法判断的具体类- 静态上下文中不允许使用变量。 - **解决方式**: 使用反射或其他工具(如 TypeToken),可以通过间接手段恢复部分类信息。 --- ##### 6. **数组与** 由于数组具有运行时类检查的能力,而依赖于编译期类检查,这使得两者之间存在冲突。因此,不能创建带有的数组。 - 错误示例: ```java List<String>[] stringLists = new List<String>[1]; // 编译报错 ``` - 替代方案: ```java List<String>[] stringLists = (List<String>[]) new List<?>[1]; ``` --- #### 总结 Java 的核心在于提升代码的安全性和复用能力。通过了解其核心概念(如类、接口、方法、通配符和类擦除),开发者能够更高效地构建灵活且健壮的应用程序。 --- ### 示例代码 以下是综合运用的知识点的简单例子: ```java public class Main { public static void main(String[] args) { Box<Integer> integerBox = new Box<>(); integerBox.setContent(10); System.out.println(integerBox.getContent()); Pair<String, Integer> pair = new Pair<>("Key", 123); System.out.println(pair.getKey() + ": " + pair.getValue()); List<Number> numbers = Arrays.asList(1, 2.5f, 3L); printNumbers(numbers); List<Object> objects = Arrays.asList("String", new Object(), 42); addObject(objects, "New Element"); } public static <T> void printNumbers(List<? extends Number> list) { for (Number number : list) { System.out.print(number.doubleValue() + " "); } System.out.println(); } public static <T> void addObject(List<? super T> list, T element) { list.add(element); } } class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } } class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值