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