这两种结构之间的根本区别是什么?

时间:2011-09-27 15:02:30

标签: function scala anonymous-function

  

可能重复:
  Difference between method and function in Scala

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之间的实际差异是什么。你能举例说明何时使用这两种结构?

1 个答案:

答案 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类。

在上面的代码片段中,x2x3都编译为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
    */
}