对本地 class 构造函数的方法引用

Method reference to local class constructor

关于对本地 class 构造函数的方法引用,SO 上有几个类似的问题,但我想稍微澄清一下其他事情。考虑以下代码:

static Callable gen(int i) {
    class X {
        int x = i;
        public String toString() { return "" + x; }
    }
    return X::new;
}

...

System.out.println(gen(0).call()); 
System.out.println(gen(1).call());

显然这将打印输出

0
1

事实证明,X class 具有 ...$X(int) 形式的构造函数(您可以通过 X.class.getDeclaredConstructors() 找到它)。

但这里有趣的是,返回的 lambda(或方法引用)并不是对构造函数 ...$X(int) 的简单引用,例如 Integer::new。他们使用预定义参数(01)在内部调用此构造函数 ...$X(int)

所以,我不确定,但看起来 JLS 中没有准确描述这种方法参考。除了本地 classes 的这种情况之外,没有其他方法可以生成这种 lambdas(具有预定义的构造函数参数)。谁能帮忙解释一下?

准确地说:

此行为在 JLS 部分中定义 §15.13.3:

If the form is ClassType :: [TypeArguments] new, the body of the invocation method has the effect of a class instance creation expression of the form new [TypeArguments] ClassType(A1, ..., An), where the arguments A1, ..., An are the formal parameters of the invocation method, and where:

  • The enclosing instance for the new object, if any, is derived from the site of the method reference expression, as specified in §15.9.2.

  • The constructor to invoke is the constructor that corresponds to the compile-time declaration of the method reference (§15.13.1).

虽然这里讨论了封闭 实例 ,但在 §15.13.3.

中没有提到捕获的变量和参数

关于你的第二个问题,你需要手动抓取修改参数:

static Callable gen(int i) {
    final int i1 = someCondition() ? i : 42;
    class X {
        int x = i1; // <-
        public String toString() { return "" + x; }
    }
    return X::new;
}

您过于关注不相关的底层细节。在字节码级别,可能有一个构造函数接受 int 参数,但在语言级别,您没有指定显式构造函数,因此,将有一个不带任何参数的默认构造函数,与任何其他 class.

当您编写 pre-Java 8 代码时,这应该会变得清楚:

static Callable<Object> gen(int i) {
    class X {
        int x = i;
        public String toString() { return "" + x; }
    }
    X x=new X();
    …

您通过其默认构造函数实例化 X,不接受任何参数。您的本地 class 捕获 i 的值,但它是如何在低级别上实现的,即 X' 构造函数具有合成 int 参数和 new 表达式会将 i 的值传递给它,是一个实现细节。

您甚至可以添加一个显式构造函数作为

        X() {}

没有任何改变。

显然,您也可以在此处将表达式 new X() 写在 lambda 表达式中,因为将表达式放在 lambda 表达式中时不会改变其语义:

    return () -> new X();

或者用short-hand的形式,方法参考

    return X::new;

它没有什么特别之处,如果您忘记了令人分心的底层细节,即使不参考规范,这种行为也是可以理解的。 X 可以捕获任意多的局部变量,构造函数的参数数量不变(在语言级别)。