关于本地类构造函数的方法引用,有几个类似的问题,但我想稍微澄清其他的事情。请考虑以下代码:
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
类具有...$X(int)
形式的构造函数(您可以通过X.class.getDeclaredConstructors()
找到它。)
但有趣的是,返回的lambda(或方法引用)不是对构造函数...$X(int)
的简单引用,例如,Integer::new
。他们在内部使用预定义的参数(...$X(int)
或0
)调用此构造函数1
。
所以,我不确定,但看起来这种方法参考在JLS中没有精确描述。除了本地类的这种情况之外,没有其他方法可以生成这种lambda(使用预定义的构造函数参数)。谁能帮忙澄清一下?
确切地说:
在JLS中描述了哪种方法参考?
是否可以使用预定义的参数创建此类方法引用任意类构造函数的任何其他方法?
答案 0 :(得分:3)
此行为在JLS部分§15.13.3中定义:
如果表单是ClassType :: [TypeArguments] new,则调用方法的主体具有new [TypeArguments] ClassType(A1,...,An)形式的类实例创建表达式的效果,其中参数A1,...,An是调用方法的形式参数,其中:
新对象的封闭实例(如果有)源自方法引用表达式的站点,如第15.9.2节中所述。
要调用的构造函数是与方法引用的编译时声明(第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;
}
答案 1 :(得分:3)
你过分关注不相关的低级细节。在字节代码级别,可能有一个构造函数接受int
参数,但在语言级别上,您没有指定显式构造函数,因此,将会有一个没有任何参数的默认构造函数,就像任何参数一样其他课程。
当您编写Java 8之前的代码时,这应该变得清晰:
static Callable<Object> gen(int i) {
class X {
int x = i;
public String toString() { return "" + x; }
}
X x=new X();
…
您通过默认构造函数实例化X
,而不是接受任何参数。您的本地类捕获 i
的值,但它是如何在低级别上执行此操作,即X
'构造函数具有合成int
参数并且new
表达式会将i
的值传递给它,是一个实现细节。
您甚至可以将显式构造函数添加为
X() {}
没有改变任何东西。
显然,你也可以在这里写一个lambda表达式中的表达式new X()
,因为表达式在置于lambda表达式中时不会改变它们的语义:
return () -> new X();
或使用它的简写形式,方法参考
return X::new;
没有什么特别之处,即使没有参考规范,如果你忘记分散注意力的低级别细节,行为也是可以理解的。 X
可以捕获任意数量的局部变量,构造函数的参数数量不会改变(在语言级别上)。