对泛型类型使用 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;
}
}
我正在尝试创建一个通用方法来处理 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;
}
}