JEP 118:运行时访问参数名称
总结
提供一种机制,通过核心反射在运行时轻松可靠地检索方法和构造函数的参数名称。
目标
主要目标是提高代码的可读性,当前代码使用逻辑上多余的注解来记录参数名称。 次要目标是通过使参数名称更广泛可用,来提高 IDE 的能力。
动机
Java 使用的是位置参数传递,即方法调用中的第一个参数作为第一个参数传递,方法调用中的第二个参数作为第二个参数传递,以此类推。许多其他系统使用名义参数传递,其中传递的是一组类似于名称:值对的内容。当 Java 代码与名义参数传递系统进行交互时,通常需要原始 Java 源代码中参数的名称,以构建正确的名称:值映射。然而,在现有的 Java SE API 中,无法可靠地检索方法或构造函数的参数名称。作为一种变通方法,各种 API 定义了它们自己的“@ParameterName
”注解,这导致源代码看起来非常杂乱。
Java 集成开发环境(IDE)现在可以自动生成用于创建接口或抽象类的具体子类型的模板代码。如果源代码可用,生成的代码中可以使用来自源代码的参数名称。如果参数名称信息能够可靠地存储在类文件中,那么在更多情况下就可以生成有用的名称。
根据所采取的方法,长期以来无法确定方法源代码中的参数数量与编译后方法中的参数数量不一致的问题也可能得到解决。
描述
所提出的方案是在版本 52.0 的类文件中创建一个可选的新 JVM 属性,以存储有关 JVM 级方法参数的信息。该信息包括:
- 参数的源级名称(如果有)
- 参数的修饰符(如果有)
类文件中的方法或构造函数的参数在源代码中可能没有任何对应的参数。例如,
-
作为一种实现选择,
javac
会在enum
构造函数前添加两个合成参数,以便编译器能够传入名称和序数信息。其他编译器,包括不同版本中的javac
实现,可以自由使用其他实现技术来传递这些信息。 -
匿名内部类的构造函数通常会在构造函数的参数列表前再添加一个参数,以允许传递外部的 this 信息。然而,在静态上下文中的匿名内部类构造函数,例如
static
初始化块,则不需要这样的参数。
鉴于此信息的可用性,核心反射机制可以提供一个 java.lang.reflect.Parameter
类,以允许检索这些信息。java.lang.reflect.Executable
中将定义一个返回 Parameter
对象数组的方法,该类是 Method
和 Constructor
的共同超类。相比于继续使用间接建模(如 Method.getParameterTypes
和 Method.getParameterAnnotations
中的情况,其中合成参数的行为未被明确定义),对参数信息进行更直接的建模是更优的选择。
为避免引入不必要的兼容性限制,应该对仅用于提供信息的参数名称和作为方法或构造函数的公共接口提供的参数名称加以区分。注解是表明特定参数名称是否作为导出接口的一个很好的选择。
替代方案
设计尚未最终确定,但替代方案包括将名称信息存储在由编译器合成的参数注解中。指示应合成那些参数注解的触发条件可以是类或接口上存在注解,甚至是类或接口上的注解的注解类型上存在的 _meta_annotation
。
测试
假设描述部分中的一般方法,大部分实现将属于几乎隐式的编译器规范领域,即 Java 编译器如何将 Java 源代码转换为类文件(或其他可执行输出格式)的规范。由于源代码到类文件的映射正在测试中,这与 JCK 测试的覆盖范围自然吻合,但要在 JCK 下进行覆盖,需要一些管理性的 Java SE 规范。
对于给定的设计,使用注解处理器来生成跨越待测试变量空间的不同代码示例相对简单。
风险与假设
通过引入一个新的 JVM 属性,这项变更的协调成本大幅增加,因为从 pack200
到 JVM 本身,所有消费 class 文件的工具都需要进行更新,以正确处理这个新属性。
在 JDK 8 的开发过程中,对多个 IDE 的支持将有助于验证此功能的操作。
依赖关系
在 JDK 组件中,完全实现此功能需要协调编译器、库和 JVM 的更改。
影响
- 兼容性:默认情况下,方法或构造函数的参数名称不应添加到其兼容性负担中。
- 性能/可扩展性:应跟踪
javac
的性能,以确保不会引入性能回归。