如何通过反射获取Java SE 8内置类方法的官方参数名称
问题描述
我打算基于Java SE 8的类开发用于静态导入的代码。我需要基于指定类的静态工厂方法自动生成Java代码,目的是避免遗漏内容、降低人工出错概率,同时减少重复复制粘贴操作。
我使用Java反射API获取目标方法的Method对象,再基于该对象生成方法签名。
例如针对LocalDate.of(int year, int month, int dayOfMonth)方法,我希望生成如下代码:
public static LocalDate date(int year, int month, int dayOfMonth) { return LocalDate.of(year, month, dayOfMonth); }
最关键的是要保证生成的方法签名参数名和JDK官方定义一致。我知道Java可以通过Parameter.getName()获取方法参数名,但参数名并不一定存在:.class文件默认不存储参数名,只有编译时添加javac的-parameters参数才会保留。我当前使用的JDK版本编译时没有开启该参数,因此无法获取到正确参数名。
我用的测试代码如下:
Method method = LocalDate.class .getMethod("of", int.class, int.class, int.class); System.out.printf("public static %s date(%s) {\n", method.getReturnType().getSimpleName(), Arrays.stream(method.getParameters()) .map(p -> String.format("%s %s", p.getType().getSimpleName(), p.getName())) .collect(Collectors.joining(", "))); System.out.printf(" return %s.%s(%s);\n", method.getDeclaringClass().getSimpleName(), method.getName(), Arrays.stream(method.getParameters()) .map(Parameter::getName) .collect(Collectors.joining(", "))); System.out.println("}");
输出结果不符合预期:
public static LocalDate date(int arg0, int arg1, int arg2) { return LocalDate.of(arg0, arg1, arg2); }
我尝试过用第三方库从Java 8 Javadoc中读取参数名,但该库不支持Java 8的Javadoc HTML格式。
我当前在macOS上通过SDKMAN!管理Java版本,使用的是AdoptOpenJDK 8.x。由于Java 8各版本的方法定义一致,只要是Java 8版本的解决方案都可以。
请问这种情况下如何获取Java SE 8内置类Method对象对应的方法参数名?
解决方案
方案1:使用JDK9及以上版本执行代码生成逻辑(最简单)
JDK9及之后的所有OpenJDK、OracleJDK发行版,内置的Java SE类编译时都默认开启了-parameters参数,直接用反射调用Parameter.getName()就能拿到和官方定义完全一致的参数名。
代码生成工具的运行JDK版本和生成代码的目标兼容版本互不影响,你完全可以用更高版本JDK运行生成逻辑,生成的代码照常放在JDK8环境下编译使用即可。
方案2:引入JDK8参数名预定义数据集
目前有现成的开源数据集收录了JDK8所有公共方法的官方参数名,直接引入对应依赖后,就可以通过方法签名直接查询到正确的参数名,不需要额外做解析操作,适合必须在JDK8环境下执行生成逻辑的场景。
方案3:解析本地JDK源码包提取参数名
你用SDKMAN安装的AdoptOpenJDK 8默认自带源码包src.zip,路径一般对应$JAVA_HOME/src.zip。你可以借助轻量Java语法解析工具读取对应类的源码,从方法定义里直接提取参数名,拿到的结果和官方定义完全一致,不需要依赖第三方数据。
内容的提问来源于stack exchange,提问作者M. Justin

