对泛型类型使用 Class 参数(例如 ArrayList)

Use Class parameter for generic type (e.g ArrayList)

我正在尝试创建一个通用方法来处理 Java 中不同类型的 ArrayList。类型大不相同,但都包含一个相同的参数,我想在这种方法中对其进行评估。

但是我无法使用给定的 class 信息创建正确的 ArrayList<type>

我做错了什么?

private String test(ArrayList<?> list, Class<?> type) {
    for(type i : list){  // for (type i : (ArrayList<type>) list){
     // do something
    }
    return "xxx"
}

private void init() {
    ArrayList<Type_1> a1 = new ArrayList<>();
    ArrayList<Type_2> a2 = new ArrayList<>();

    String s1 = test(a1, Type_1.class);
    String s2 = test(a2, Type_2.class);
}

更新 找到解决方案

private String test(ArrayList<?> list) {
    for (Object i : list){
        try {
            Method m = i.getClass().getMethod("getName", null);
            System.out.println(m.invoke(i));
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            //Handle Exception
        }
    }
 }

您需要显式声明类型参数:

  private <T> String test(ArrayList<T> list, Class<T> type) {
      for (T i : list) {
         // do something with 'i'
      }
      return "xxx"
  }

如果不需要Class<T>参数,可以省略;例如

  private <T> String test(ArrayList<T> list) {
      for (T i : list) {
         // do something
      }
      return "xxx"
  }

如果您打算创建 class 的实例或 class.[=16= 的数组,您通常只需要传递一个 Class<T> type 对象]


注释掉的代码版本无法编译,因为它混合了 compile-time 和运行时类型。在

for (type i : (ArrayList<type>) list) {

编译器需要在编译时知道 type 表示的是什么类型。但它是一个运行时变量。但这甚至在此之前也是无效的,因为 Java 语法需要此时类型的标识符……而不是变量的标识符。

我创建了一个与两种解决方案相关的混合答案:

实现接口

第一个是用一个简单的接口 MyType 完成的,它为 类 Type_1 合同方法 定义=13=]。这是干净和正确的 Java 方式。这样编译器就会告诉你是否可以执行某些操作。这也表明初学者在实施想法的概念上存在问题。

缺点是所有 类 都必须实现该接口(可以在其继承层次结构的任何位置定义)。

但是 Java 都是关于类型安全和编译器警告的优点。所以这显然是首选方式。

使用反射

可以使用反射来完成这项任务,是的。但不一定是个好主意。用反射,有多个问题:

  • 您将遇到运行时错误或必须处理这些异常
  • 该项目将启动,但如果您的设计、您的概念存在缺陷,这将很难查明
  • 很多库不能很好地处理反射(面向方面​​的编程、应用程序容器、GraalVM NativeImage 等特殊编译器等)
  • 使用反射会比较慢并且消耗更多内存

因此,如果远离 Reflection 可能且容易,您应该避开它。特别是如果这有一个如此简单的正确解决方案。

(我还用反射清理了你的代码,那里有一些小的不一致导致它无法编译)

代码:

package Whosebug;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class SimpleInterfacing {
    interface MyType {
        String getName();
    }
    static class Type_1 implements MyType {
        @Override public String getName() {
            return "This is type 1";
        }
    }
    static class Type_2 implements MyType {
        @Override public String getName() {
            return "This is type 2";
        }
    }

    private String test(final ArrayList<? extends MyType> list) {
        String returnValue = null;
        for (final MyType t : list) {
            // do something
            System.out.println("Got name: " + t.getName());
            returnValue = t.getName();
        }
        return returnValue; // returns last value, null is lists are empty
    }

    private void init() {
        final ArrayList<Type_1> a1 = new ArrayList<>();
        a1.add(new Type_1());

        final ArrayList<Type_2> a2 = new ArrayList<>();
        a2.add(new Type_2());

        {
            final String s1 = test(a1);
            System.out.println("s1 is " + s1);
        }
        {
            final String s2 = test(a2);
            System.out.println("s2 is " + s2);
        }
        {
            test_reflection(a1);
            test_reflection(a2);
        }
    }

    public static void main(final String[] args) {
        new SimpleInterfacing().init();
    }

    private String test_reflection(final ArrayList<?> list) {
        for (final Object i : list) {
            try {
                final Method m = i.getClass().getMethod("getName");
                System.out.println("Invoked: " + m.invoke(i));
            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                //Handle Exception
                ex.printStackTrace();
            }
        }
        return null;
    }



}