想象一下这段代码:
class Foo {
println("in Foo")
def foo(a: Int) = a + 1
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我们调用:
new Foo().foo _
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,将创建类Foo的实例:
in Foo
res0: (Int) => Int = <function1>
Run Code Online (Sandbox Code Playgroud)
但是,如果我们调用它:
new Foo().foo(_)
Run Code Online (Sandbox Code Playgroud)
Foo的构造函数不会被调用:
res1: (Int) => Int = <function1>
Run Code Online (Sandbox Code Playgroud)
如果我们说:
res1(7)
Run Code Online (Sandbox Code Playgroud)
那是Foo实例化的时候:
in Foo
res2: Int = 8
Run Code Online (Sandbox Code Playgroud)
为什么Eta扩展与部分函数应用程序在类实例化方面有所不同?
天哪,这是一个微妙的问题,但据我所知,它完全遵循Scala 规范。我将引用 2.9 版规范中的内容。
\n\n对于您的第一个示例:正如您所说,您通过方法值(\xc2\xa76.7)的特殊情况看到了 eta 扩展:
\n\nThe expression e _ is well-formed if e is of method type or if e is a call-by-name parameter. If e is a method with parameters, e _ represents e converted to a function type by eta expansion.
\xc2\xa76.26.5 中给出了 eta 扩展的算法,您可以按照该算法对表达式进行以下替换new Foo().x1 _
:
{\n val x1 = new Foo();\n (y1: Int) => x1.(y1);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n这意味着当使用 eta 扩展时,所有子表达式都会在转换发生时进行计算(如果我正确理解了短语“最大子表达式”的含义),最终表达式是创建匿名函数。
\n\n在第二个示例中,这些额外的括号意味着编译器将查看 \xc2\xa76.23 (特别是“匿名函数的占位符语法”)并直接创建匿名函数。
\n\nAn expression (of syntactic category Expr) may contain embedded underscore symbols _ at places where identifiers are legal. Such an expression represents an anonymous function where subsequent occurrences of underscores denote successive parameters.
在这种情况下,按照该部分中的算法,您的表达式最终将是这样的:
\n\n(x1: Int) => new Foo().foo(x1)\n
Run Code Online (Sandbox Code Playgroud)\n\n差异是微妙的,正如 @Antoras 很好地解释的那样,只有在存在副作用代码时才真正显示出来。
\n\n请注意,针对涉及按名称调用代码块的情况正在修复错误(例如,请参阅此问题、此错误和此错误)。
\n\n后记:在这两种情况下,匿名函数(x1:Int) => toto
都会扩展为
new scala.Function1[Int, Int] {\n def apply(x1: Int): Int = toto\n}\n
Run Code Online (Sandbox Code Playgroud)\n