在运行时获取泛型类型:在某些情况下有效,但在其他情况下无效 - 为什么?
Get generic type at runtime: works in some cases, but not in others - why?
我正在尝试获取 class 或接口实现的通用类型。我知道这有一些风险和怪癖,但我试图了解什么是可能的。
这是我的示例代码:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.LinkedList;
public class Main {
public interface MyInterface<T> {
}
public static class BaseImpl<T> implements MyInterface<T> {
}
public static class TypedImpl extends BaseImpl<Integer> {
}
public static void main(String[] args) throws Exception {
LinkedList<MyInterface<Integer>> instances = new LinkedList<>();
// 1. anonymous class from interface
instances.add(new MyInterface<Integer>() {
});
// 2. class extending interface
instances.add(new BaseImpl<Integer>());
// 3. class with pre-defined generic type
instances.add(new TypedImpl());
for (MyInterface<Integer> instance : instances) {
System.out.println("----------------------------------------------");
Class clazz = instance.getClass();
Type genericSuper = clazz.getGenericSuperclass();
Type[] genericInterfaces = clazz.getGenericInterfaces();
Class target = null;
System.out.println("class: " + clazz.getName());
System.out.println("generic super: " + genericSuper);
System.out.println("generic interfaces: " + Arrays.asList(genericInterfaces));
// attempt to 'extract' generic type
if (genericSuper instanceof ParameterizedType) {
target = getGeneric((ParameterizedType) genericSuper);
} else if (genericInterfaces.length > 0) {
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
target = getGeneric((ParameterizedType) genericInterface);
if (null != target) {
break;
}
}
}
}
System.out.println("TARGET: " + target);
}
}
// attempt to get type argument
public static Class getGeneric(ParameterizedType type) {
if (MyInterface.class.isAssignableFrom((Class) type.getRawType())) {
Type typeArg = type.getActualTypeArguments()[0];
try {
return (Class) typeArg;
} catch (ClassCastException e) {
System.out.println("cast exception for '" + typeArg + "'");
}
}
return null;
}
}
这个输出是:
----------------------------------------------
class: Main
generic super: class java.lang.Object
generic interfaces: [Main.Main$MyInterface<java.lang.Integer>]
TARGET: class java.lang.Integer
----------------------------------------------
class: Main$BaseImpl
generic super: class java.lang.Object
generic interfaces: [Main.Main$MyInterface<T>]
cast exception for 'T'
TARGET: null
----------------------------------------------
class: Main$TypedImpl
generic super: Main.Main$BaseImpl<java.lang.Integer>
generic interfaces: []
TARGET: class java.lang.Integer
所以,我的目标是让 target
变量的值为 Integer.class
。对于匿名 class (#1) 和显式类型实现 (#3),我能够按预期找到目标。为什么它在这些实例中起作用而不是在#2(BaseImpl 实例)的情况下?还有另一种方法可以做到这一点吗? (我知道通过实现 class 的构造函数传递目标 Class
的常见解决方法,但是我有兴趣动态地执行此操作)
我参考了以下资源:
- Get type of a generic parameter in Java with reflection
- Get generic type of class at runtime
- How to get a class instance of generics type T
非常感谢,
亚伯
在情况 1 和 3 中,类型是 classes Main
和 Main$TypedImpl
上的反射数据的一部分,因此您可以访问它。
在情况 2 中,您只有实例数据,由于类型擦除,该数据不包含任何类型信息。因此,您无法在此处访问通用类型。
进一步说明:
在运行时,系统无法区分 BaseImpl<Integer> i;
和 BaseImpl<String> s;
,即由于类型擦除,变量 i
和 s
都是类型 BaseImpl
。
相比之下,TypedImpl t;
已知属于 TypedImpl
类型,使用反射可以提取 extends
或 implements
语句中提供的通用类型。
旁注,因为您正在尝试了解什么是可能的:
如果您将 BaseImpl
定义为 BaseImpl<T extends Number> implements MyInterface<T>
,您可以从 class BaseImpl
上的反射数据中提取 T
的上限,即您将能够知道 T
必须是 Number
或子类型(或接口情况下的实现)。
来自 http://tutorials.jenkov.com/java-reflection/generics.html(粗体是我的)
Using Java Generics typically falls into one of two different
situations:
Declaring a class/interface as being parameterizable. Using a
parameterizable class. When you write a class or interface you can
specify that it should be paramerizable. This is the case with the
java.util.List interface. Rather than create a list of Object you can
parameterize java.util.List to create a list of say String.
When runtime inspecting a parameterizable type itself, like
java.util.List, there is no way of knowing what type is has been
parameterized to. This makes sense since the type can be parameterized
to all kinds of types in the same application. But, when you inspect
the method or field that declares the use of a parameterized type, you
can see at runtime what type the paramerizable type was parameterized
to. In short:
You cannot see on a type itself what type it is parameterized to a
runtime, but you can see it in fields and methods where it is used and
parameterized. Its concrete parameterizations in other words.
具体情况在页面上有说明
我正在尝试获取 class 或接口实现的通用类型。我知道这有一些风险和怪癖,但我试图了解什么是可能的。
这是我的示例代码:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.LinkedList;
public class Main {
public interface MyInterface<T> {
}
public static class BaseImpl<T> implements MyInterface<T> {
}
public static class TypedImpl extends BaseImpl<Integer> {
}
public static void main(String[] args) throws Exception {
LinkedList<MyInterface<Integer>> instances = new LinkedList<>();
// 1. anonymous class from interface
instances.add(new MyInterface<Integer>() {
});
// 2. class extending interface
instances.add(new BaseImpl<Integer>());
// 3. class with pre-defined generic type
instances.add(new TypedImpl());
for (MyInterface<Integer> instance : instances) {
System.out.println("----------------------------------------------");
Class clazz = instance.getClass();
Type genericSuper = clazz.getGenericSuperclass();
Type[] genericInterfaces = clazz.getGenericInterfaces();
Class target = null;
System.out.println("class: " + clazz.getName());
System.out.println("generic super: " + genericSuper);
System.out.println("generic interfaces: " + Arrays.asList(genericInterfaces));
// attempt to 'extract' generic type
if (genericSuper instanceof ParameterizedType) {
target = getGeneric((ParameterizedType) genericSuper);
} else if (genericInterfaces.length > 0) {
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
target = getGeneric((ParameterizedType) genericInterface);
if (null != target) {
break;
}
}
}
}
System.out.println("TARGET: " + target);
}
}
// attempt to get type argument
public static Class getGeneric(ParameterizedType type) {
if (MyInterface.class.isAssignableFrom((Class) type.getRawType())) {
Type typeArg = type.getActualTypeArguments()[0];
try {
return (Class) typeArg;
} catch (ClassCastException e) {
System.out.println("cast exception for '" + typeArg + "'");
}
}
return null;
}
}
这个输出是:
----------------------------------------------
class: Main
generic super: class java.lang.Object
generic interfaces: [Main.Main$MyInterface<java.lang.Integer>]
TARGET: class java.lang.Integer
----------------------------------------------
class: Main$BaseImpl
generic super: class java.lang.Object
generic interfaces: [Main.Main$MyInterface<T>]
cast exception for 'T'
TARGET: null
----------------------------------------------
class: Main$TypedImpl
generic super: Main.Main$BaseImpl<java.lang.Integer>
generic interfaces: []
TARGET: class java.lang.Integer
所以,我的目标是让 target
变量的值为 Integer.class
。对于匿名 class (#1) 和显式类型实现 (#3),我能够按预期找到目标。为什么它在这些实例中起作用而不是在#2(BaseImpl 实例)的情况下?还有另一种方法可以做到这一点吗? (我知道通过实现 class 的构造函数传递目标 Class
的常见解决方法,但是我有兴趣动态地执行此操作)
我参考了以下资源:
- Get type of a generic parameter in Java with reflection
- Get generic type of class at runtime
- How to get a class instance of generics type T
非常感谢,
亚伯
在情况 1 和 3 中,类型是 classes Main
和 Main$TypedImpl
上的反射数据的一部分,因此您可以访问它。
在情况 2 中,您只有实例数据,由于类型擦除,该数据不包含任何类型信息。因此,您无法在此处访问通用类型。
进一步说明:
在运行时,系统无法区分 BaseImpl<Integer> i;
和 BaseImpl<String> s;
,即由于类型擦除,变量 i
和 s
都是类型 BaseImpl
。
相比之下,TypedImpl t;
已知属于 TypedImpl
类型,使用反射可以提取 extends
或 implements
语句中提供的通用类型。
旁注,因为您正在尝试了解什么是可能的:
如果您将 BaseImpl
定义为 BaseImpl<T extends Number> implements MyInterface<T>
,您可以从 class BaseImpl
上的反射数据中提取 T
的上限,即您将能够知道 T
必须是 Number
或子类型(或接口情况下的实现)。
来自 http://tutorials.jenkov.com/java-reflection/generics.html(粗体是我的)
Using Java Generics typically falls into one of two different situations:
Declaring a class/interface as being parameterizable. Using a parameterizable class. When you write a class or interface you can specify that it should be paramerizable. This is the case with the java.util.List interface. Rather than create a list of Object you can parameterize java.util.List to create a list of say String.
When runtime inspecting a parameterizable type itself, like java.util.List, there is no way of knowing what type is has been parameterized to. This makes sense since the type can be parameterized to all kinds of types in the same application. But, when you inspect the method or field that declares the use of a parameterized type, you can see at runtime what type the paramerizable type was parameterized to. In short:
You cannot see on a type itself what type it is parameterized to a runtime, but you can see it in fields and methods where it is used and parameterized. Its concrete parameterizations in other words.
具体情况在页面上有说明