您的问题
1。这种行为是在 JLS/JVMS 规范中正式定义的(如果是,在哪里?),还是由实现该语言的不同供应商决定?
Java 语言规范似乎专门针对not describe reflection:
因此,本规范没有详细描述反射。
而是将反射的全部行为留给 API 记录(即在 Javadoc 中)。
但是,Java 虚拟机规范确实解释了generic information must be emitted by a compiler:
4.7.9。 Signature 属性
Signature 属性是ClassFile、field_info 或method_info 结构(§4.1、§4.5、§4.6)的属性表中的固定长度属性。 Signature 属性记录类、接口、构造函数、方法或字段的签名(第 4.7.9.1 节),其在 Java 编程语言中的声明使用类型变量或参数化类型。有关这些构造的详细信息,请参阅Java 语言规范,Java SE 15 版。
[...]
4.7.9.1。签名
签名对用 Java 编程语言编写的声明进行编码,这些声明使用 Java 虚拟机类型系统之外的类型。它们支持反射和调试,以及只有class 文件可用时的编译。
Java 编译器必须为其声明使用类型变量或参数化类型的任何类、接口、构造函数、方法或字段发出签名。具体来说,Java 编译器必须发出:
-
任何类或接口声明的类签名,它要么是泛型的,要么具有作为超类或超接口的参数化类型,或两者兼而有之。
-
任何方法或构造函数声明的方法签名,它要么是泛型的,要么具有类型变量或参数化类型作为返回类型或形式参数类型,要么在throws 子句中具有类型变量,或任何组合其中。
如果方法或构造函数声明的 throws 子句不涉及类型变量,则编译器可能会将声明视为没有 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 字段。我们不是查看所述字段引用的实例(即运行时对象)。知道该字段的泛型类型实际上与知道该字段的名称是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
我们可以看到我们知道list引用的实例是java.util.ArrayList的实例。但是从那里我们可以确定的是ArrayList 类是通用的并且具有单个类型参数E。我们无法确定list 字段是否被分配了ArrayList 类型参数为Integer。换句话说,ArrayList 实例本身不知道它声明包含什么类型的元素——该信息已被删除。
换句话说,list 字段的类型在运行时是已知的,但 ArrayList 实例(即对象在运行时创建)只知道它是 @987654358 @。