scala> val x1 : (Int) => Int = (x) => x + 1
x1: (Int) => Int = <function>
scala> def x2 (x : Int) = x + 1
x2: (Int)Int
scala> x1(1)
res0: Int = 2
scala> x2(1)
res1: Int = 2
x1和x2之间的实际差异是什么。你能举例说明何时使用这两种结构?
答案 0 :(得分:2)
在Scala中,这两个构造从用户的角度看起来很相似,但与JVM的观点完全不同。 x1
是一个“函数对象”,x2
是一种方法。
class Test
{
val x1 = (x: Int) => x + 3
def x2(x: Int) = {
def x3(y: Int) = y + 10
x3(x) + 3
}
}
在Scala中,def
定义了一个直接编译为JVM方法的方法,它必须属于某个Java类。
在上面的代码片段中,x2
和x3
都编译为JVM方法,属于class Test
,甚至x3
在方法内定义(嵌套方法)。
您可以在编译代码后使用javap -private Test
来验证这一点。它将输出以下消息:
brianhsu@USBGentoo ~/test $ javap -private Test
Compiled from "test.scala"
public class Test extends java.lang.Object implements scala.ScalaObject{
private final scala.Function1 x1;
public scala.Function1 x1();
public int x2(int);
private final int x3$1(int);
public Test();
}
您可以看到x2
只是一个普通的Java方法,并且x3被重命名为x3 $ 1(以避免名称冲突,如果在其他方法中有另一个x3
),但它仍然是从JVM的角度看普通的Java方法。
我使用这个术语来避免混淆,这是你用val x1 = (x: Int) => x + 1
之类的定义。
使用它时可能看起来像方法,但事实上它与您使用def
定义的方法完全不同。
然后val x1 = (x: Int) => x + 2
意味着什么?
嗯,在Scala中有一些特性叫做Function0,Function1,Function2,Function3 ...... Function22。
它们看起来如下(我简化了它):
// T1 is the type of parameter 1, R is the type of return value
triat Function1[T1, R] {
def apply(t1: T1): R
}
当您编写val x1 = (x: Int) => x + 2
时,Scala编译器将生成一个实现特征Function1的对象,它可能如下所示:
val t = new Function1[Int, Int] {
def apply(t1: Int) = t1 + 2
}
当你写x1(3)
时,事实上Scala只是将它转换为t.apply(3)。
所以,一个函数对象不是一个方法,它只是一个普通的Java对象,它有一个名为apply
的方法,而Scala编译器给你的语法糖就是'使用时必须明确调用apply
。
您可以再次使用javap
验证这一点。
brianhsu@USBGentoo ~/test $ javap Test\$\$anonfun\$1
Compiled from "test.scala" public final class Test$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable{
public static final long serialVersionUID;
public static {};
public final int apply(int);
public int apply$mcII$sp(int);
public final java.lang.Object apply(java.lang.Object);
public Test$$anonfun$1(Test);
}
brianhsu@USBGentoo ~/test $ javap -private Test
Compiled from "test.scala" public class Test extends java.lang.Object implements scala.ScalaObject{
private final scala.Function1 x1;
public scala.Function1 x1();
public int x2(int);
private final int x3$1(int);
public Test();
}
您会注意到有一个名为Test$$anonfun$1.class
的额外.class文件,即(x: Int) => x + 2
的类,您会注意到有x1
个私有变量是Function1
x1()
方法会返回scala.Function1
。
有x1()
方法,因为Scala实现了统一访问原则。但问题是x1()
方法只返回Test$$anonfun$1
类的函数对象实例。
也许方法和功能看起来相同,但它们是不同的东西。 Scala编译器帮助我们一起使用它们而不需要花费太多精力。
大部分时间你都不会关心它们之间的区别,但实际上有时某些东西需要一个函数对象而你只有方法。
在这种情况下,编译器会告诉您在方法名称之后添加_
以将其提升为函数对象。
这是一个有趣的代码示例,它显示了方法和函数对象之间的区别:您可以定义一个方法,取25个参数,但无法定义一个超过22个参数的函数对象。
class Test2
{
def x (
x01: Int, x02: Int, x03: Int, x04: Int, x05: Int,
x06: Int, x07: Int, x08: Int, x09: Int, x10: Int,
x11: Int, x12: Int, x13: Int, x14: Int, x15: Int,
x16: Int, x17: Int, x18: Int, x19: Int, x20: Int,
x21: Int, x22: Int, x23: Int, x24: Int, x25: Int
) = 0
// Compile error:
// implementation restricts functions to 22 parameters
/*
val y = (
x01: Int, x02: Int, x03: Int, x04: Int, x05: Int,
x06: Int, x07: Int, x08: Int, x09: Int, x10: Int,
x11: Int, x12: Int, x13: Int, x14: Int, x15: Int,
x16: Int, x17: Int, x18: Int, x19: Int, x20: Int,
x21: Int, x22: Int, x23: Int, x24: Int, x25: Int
) => 0
*/
}