您的问题
1.此行为是否在JLS/JVMS规范中正式定义(如果是,在哪里?),还是取决于实现该语言的不同供应商?
这个
Java语言规范
似乎特别
not describe reflection
:
因此,本说明书没有详细描述反射。
但是,将反射的全部行为留给API记录(即在Javadoc中)。
然而
Java虚拟机器规格
确实解释了这一点
generic information must be emitted by a compiler
:
4.7.9. 这个
Signature
属性
这个
签名
attribute是a的属性表中的固定长度属性
ClassFile
,
field_info
,或
method_info
结构(§4.1、§4.5、§4.6)。A.
签名
属性记录了类、接口、构造函数、方法或字段的签名(§4.7.9.1),其在Java编程语言中的声明使用类型变量或参数化类型。请参阅
Java语言规范,Java SE 15版
有关这些构造的详细信息。
[...]
4.7.9.1. 签名
签名
对使用Java虚拟机类型系统之外的类型的Java编程语言编写的声明进行编码。它们支持反射和调试,以及仅在特定情况下进行编译
class
文件可用。
Java编译器必须为其声明使用类型变量或参数化类型的任何类、接口、构造函数、方法或字段发出签名。具体来说,Java编译器必须发出:
-
任何类或接口声明的类签名,可以是泛型的,也可以具有参数化类型作为超类或超接口,或者两者兼而有之。
-
任何方法或构造函数声明的方法签名,该声明要么是泛型的,要么具有类型变量或参数化类型作为返回类型或形式参数类型,要么在
throws
条款或其任何组合。
如果
投掷
方法或构造函数声明的子句不涉及类型变量,那么编译器可能会将该声明视为没有类型变量
投掷
子句,用于发出方法签名。
-
任何类型使用类型变量或参数化类型的字段、形式参数或局部变量声明的字段签名。
[...]
2.是否可以将反射方法应用于局部变量
foo
得到类似的东西
Foo<java.lang.String>
?
否,因为局部变量不可反射访问。至少不是直接用Java语言。但假设他们是。您有:
Foo foo = new Foo<String>();
将反映的是左手侧。这是一种原始类型,所以你只知道
foo
是
Foo
。您无法判断右侧创建的实例是用参数化的
String
.
一些澄清(希望)
当我们说“泛型在运行时被擦除”时,我们并不是指在这种情况下。静态定义的可反射访问的构造类型(如字段)保存在字节码中。例如,以下内容:
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.List;
public class Main {
private static List<? extends Number> list = new ArrayList<Integer>();
public static void main(String[] args) throws Exception {
Field field = Main.class.getDeclaredField("list");
// Due to List being a generic type the returned Type is actually
// an instance of java.lang.reflect.ParameterizedType
Type genericType = field.getGenericType();
System.out.println("Generic Type = " + genericType);
// The raw type can be gotten from the ParameterizedType. Here the
// returned Type will actually be an instance of java.lang.Class
Type rawType = ((ParameterizedType) genericType).getRawType();
System.out.println("Raw Type = " + rawType);
// The ParameterizedType gives us access to the actual type
// arguments declared. Also, since a bounded wildcard was used
// the returned Type is actually an instance of
// java.lang.reflect.WildcardType
Type typeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[0];
System.out.println("Type Argument = " + typeArgument);
// We know in this case that there is a single upper bound. Here
// the returned Type will actually be an instance of java.lang.Class
Type upperBound = ((WildcardType) typeArgument).getUpperBounds()[0];
System.out.println("Upper Bound = " + upperBound);
}
}
将输出:
Generic Type = java.util.List<? extends java.lang.Number>
Raw Type = interface java.util.List
Type Argument = ? extends java.lang.Number
Upper Bound = class java.lang.Number
所有这些信息都在源代码中。请注意,我们正在反思
list
领域
我们是
不
查看所述字段引用的实例(即运行时对象)。知道字段的泛型类型与知道字段的名称没有什么不同
列表
.
我们不知道的是
ArrayList
参数化为
Integer
.将上述内容更改为:
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;
public class Main {
private static List<? extends Number> list = new ArrayList<Integer>();
public static void main(String[] args) {
Class<?> clazz = list.getClass();
System.out.println("Class = " + clazz);
TypeVariable<?> typeParameter = clazz.getTypeParameters()[0];
System.out.println("Type Parameter = " + typeParameter);
}
}
输出:
Class = class java.util.ArrayList
Type Parameter = E
我们可以看到,我们知道引用的实例
列表
是以下实例
java.util.ArrayList
但从那里我们只能确定
数组列表
类是泛型的,只有一个类型参数
E
我们无法确定
列表
字段被分配了
数组列表
类型参数为
整数
换言之
数组列表
实例本身不知道它声明包含该信息的元素类型已被擦除。
换句话说
列表
字段的类型在运行时是已知的,但
数组列表
实例(即对象)
在运行时创建
)只知道这是一个
数组列表
.