Java 记录通过反射不可见的构造函数

Java record constructor invisible through reflection

我正在玩 Java 15 的新记录功能,以及它如何与反射交互。我 运行 遇到了一些奇怪的行为,有时我可以通过反射访问记录的构造函数,有时则不能。例如,给定以下 Java 文件:

Recording.java:

public class Recording {
    public static void main(String[] args) {
        System.out.println("Constructors: " + MainRecord.class.getConstructors().length);
        System.out.println("Methods: " + MainRecord.class.getDeclaredMethods().length);
    }

    record MainRecord(int i, String s) {}
}

其行为如下:

❯ javac --enable-preview --release 15 Recording.java
Note: Recording.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
❯ java --enable-preview Recording
Constructors: 0
Methods: 5

换句话说,对 getConstructors() 的调用没有找到任何构造函数(而对 `getDeclaredMethods() 的调用确实找到了方法)。我不明白为什么不,因为构造函数确实存在:

❯ javap Recording$MainRecord
Compiled from "Recording.java"
final class Recording$MainRecord extends java.lang.Record {
  Recording$MainRecord(int, java.lang.String);
  public final java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public int i();
  public java.lang.String s();
}

(将记录放在单独的 Java 文件中会得到相同的结果。)

但是,如果我从 JShell 执行相同的操作:

❯ jshell --enable-preview
|  Welcome to JShell -- Version 15
|  For an introduction type: /help intro

jshell> record JShellRecord(int i, String s) {}
|  created record JShellRecord

jshell> JShellRecord.class.getConstructors().length
 ==> 1

那么,现在它确实找到了构造函数。

这是我正在使用的 Java 版本:

❯ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15+36, mixed mode, sharing)

编译和 运行ning 来自 Java 14 的相同程序确实有效:

❯ java -version
openjdk version "14.0.2" 2020-07-14
OpenJDK Runtime Environment AdoptOpenJDK (build 14.0.2+12)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14.0.2+12, mixed mode, sharing)
❯ javac --enable-preview --release 14 Recording.java
Note: Recording.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
❯ java --enable-preview Recording
Constructors: 1
Methods: 5

我知道在 Java 15 中,与 Java 14 相比,对记录的反射有一些限制,但如果我正确阅读 JEP ,那些只适用于修改。查找(并可能调用)构造函数似乎不适用。

谁能告诉我这是怎么回事?我需要做什么才能通过反射在Java 15中看到记录的构造函数?

getConstructors() returns public 仅限构造函数。使用 getDeclaredConstructors() 获取所有构造函数。

您的声明 record MainRecord(int i, String s) {} 缺少 public 修饰符,因此它创建了一个非 public class 和一个非 public 构造函数。参见 JLS15-preview, §8.10.4

The implicitly declared canonical constructor has the same access modifier as the record class R, unless the record class lacks an access modifier, in which case the canonical constructor has package access

这确实与 JDK 14 预览版不同。 the JDK 15 preview document 的开头说:

The changes are the same as those in the first preview of Records in Java SE 14, except for the following:

  • 8.10.4 Removed requirement that canonical constructor must be public. Any access modifier must provide at least as much access as the record class. If a canonical constructor is implicitly declared, then its access modifier is the same as the record class.

看来,在 JShell 中创建的顶层 classes 是隐式 public.

> jdk-15\bin\jshell --enable-preview
|  Welcome to JShell -- Version 15
|  For an introduction type: /help intro

jshell> record JShellRecord(int i, String s) {}
|  created record JShellRecord

jshell> JShellRecord.class.getConstructors()[0]
 ==> public JShellRecord(int,java.lang.String)

jshell> java.lang.reflect.Modifier.isPublic(JShellRecord.class.getModifiers())
 ==> true

jshell>