如何获取方法中传递的集合的元素类型?

Jor*_*Joh 2 java generics reflection

假设我有一些方法接受List<?>

public void method(List<?> list) {
    // some logic
}
Run Code Online (Sandbox Code Playgroud)

如何在运行时获取元素类型?是否可以?

我发现了这些建议(在这里):

  1. ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0](不起作用,List是一个接口并且没有超类)
// ChatGPT's work

public class App {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        List<Integer> integerList = new ArrayList<>();

        Type stringType = getListElementType(stringList);
        Type integerType = getListElementType(integerList);

        System.out.println("String List Element Type: " + stringType.getTypeName());
        System.out.println("Integer List Element Type: " + integerType.getTypeName());
    }

    private static Type getListElementType(List<?> list) {
        Type genericType = list.getClass().getGenericSuperclass();

        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            return parameterizedType.getActualTypeArguments()[0];
        }

        throw new IllegalArgumentException("Unable to determine list element type.");
    }
}
Run Code Online (Sandbox Code Playgroud)

控制台输出:

String List Element Type: E
Integer List Element Type: E
Run Code Online (Sandbox Code Playgroud)
  1. 在创建通用类实例期间将相应的类存储为字段(不起作用,List是只读的,而且它不能有任何实例字段,因为它是一个接口)
String List Element Type: E
Integer List Element Type: E
Run Code Online (Sandbox Code Playgroud)
  1. TypeTools。我还需要一些超类型

TypeResolver 类提供了以下一些方法:

Type reify(Type type, Class<S> context)

使用上下文中的类型变量信息返回完全具体化的类型。

Type reify(Type genericType)

使用泛型声明中的信息返回完全具体化的 genericType。

Class<?>[] resolveRawArguments(Class<T> type, Class<S> subType)

使用子类型中的类型变量信息解析类型的原始参数。

Class<?> resolveRawArgument(Class<T> type, Class<S> subType)

使用子类型中的类型变量信息解析类型的原始参数。

Type resolveGenericType(Class<?> type, Type subType)

使用子类型中的类型变量信息解析泛型类型。

Class<?> resolveRawClass(Type genericType, Class<?> subType)

使用子类型中的类型变量信息解析 genericType 的原始类。

rzw*_*oot 6

如何在运行时获取元素类型?是否可以?

不。

泛型是编译器想象出来的。它们是一个链接使用类型的地方的工具,告诉编译器:这些类型需要相同 - 注入强制转换才能使其工作。如果您可以证明类型不对齐,则会生成编译错误。然后摆脱这一切

唯一剩下的是签名中的泛型,但剩下的是您在源代码中编写的确切内容。不是实际类型。换句话说,给定:

public class MyExample<E> {
  public void addAll(Collection<E> element) {
    List<String> x = new ArrayList<String>();
    check(x);
  }

  public void check(Object o) {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

x变量的泛型完全消失了。没有什么 check可以恢复它是一个String. new ArrayList<String>()a和 a的内存占用量new ArrayList<Integer>()100% 相同- 这不仅仅是“没有 API 来获取此信息”,而是更严格:这是不可能的

可以为类型 decl 本身以及方法element的参数获取泛型addAll- 但您实际上得到的是字母E。这是没用的。

所以,重复一遍:不。不可能

TypeTools 的作用是滥用签名的生存方式,并将其与以下认识结合起来:

List<String> list = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)

泛型部分被完全消除,其中:

List<String> list = new ArrayList<String>() {};
Run Code Online (Sandbox Code Playgroud)

它不是。这是因为大量的黑客攻击:

  • ArrayList 不是最终的。如果是的话,上面的内容就不会编译。
  • 上面的代码几乎与第一个代码片段相同,但在含义上完全不同:这是声明一个具有匿名名称的全新类,定义为extends ArrayList<String>,并且具有类主体{}(如所示,它不添加任何内容)。类扩展的是“签名”,因此不会被删除,因此,在运行时您可以恢复这些东西。

然而,因为它引入了一大堆警告,并且泛型是字面意思,所以这不是一个好主意。例如,如果我写:

public <E> createList() {
  return new ArrayList<E>() {}
}
Run Code Online (Sandbox Code Playgroud)

这工作得很好,可以用例如:List<String> myList = ClassThatIsIn.<String>createList();- 尽管如此,SuperTypeTokens / typetools / 其他此类库在运行时唯一可以返回的是E- 字面意思。字母 E. 不String- 这是不可能的,因为它已被完全擦除。

因此,您的超级类型令牌附带一本手册和一长串注意事项。更一般地说,除了设计用于携带此类信息而没有其他任何内容(超类型令牌)之外的任何类型,都不是一个好主意。一种不执行任何操作的类型,它的全部作用就是供您编写SuperTypeToken<List<String>> myTokenRepresentingListOfStrings = new SuperTypeToken<List<String>>() {}

如果您需要传达泛型可以捕获的内容(即不仅仅是一个类型,还包括该类型上的任何泛型),那么这种奇怪的黑客技术可能会很有用。例如,a Map<String, List<? extends Number>>- 不只是Map,不,整个事情 -java.lang.Class不能代表那个)。不要编写一个可以确定泛型是什么的方法。

没有解决方法。回到你最初的问题,考虑到:

public void method(List<?> list) {
    // some logic
}
Run Code Online (Sandbox Code Playgroud)

答案是。一大堆博客文章和文章都在拐弯抹角地提出了实现“是”的方法,但它们都是不正确的。没有办法做到。时期。超级类型标记/类型工具是完全不同的东西。没有办法使用这样的工具来让其成为“是”。