Scala中的apply函数是什么?

What is the apply function in Scala?

我从来没有从那些做作的、不加修饰的名词(一个AddTwo类有一个apply加两个!)示例。

我知道这是语法上的甜头,所以(我从上下文推断)它的设计一定是为了让一些代码更直观。

具有apply函数的类给出了什么含义?它的用途是什么?它有什么目的使代码更好(解组、逐字逐句名词等)?

当在伴生对象中使用时,它有什么帮助?


数学家们有他们自己的一些有趣的方式,因此他们没有像我们的程序员所说的那样说"然后我们称函数f传递它x作为参数",而是说"将函数f应用到它的参数x"。

In mathematics and computer science, Apply is a function that applies
functions to arguments.
Wikipedia

apply的作用是缩小scala中面向对象范式和功能范式之间的差距。scala中的每个函数都可以表示为一个对象。每个函数也都有一个OO类型:例如,一个接受Int参数并返回Int的函数将有一个Function1[Int,Int]的OO类型。

1
2
3
4
5
 // define a function in scala
 (x:Int) => x + 1

 // assign an object representing the function to a variable
 val f = (x:Int) => x + 1

因为在scala f中,所有的东西都是一个对象,所以现在可以将其视为对Function1[Int,Int]对象的引用。例如,我们可以调用从Any继承的toString方法,这对于纯函数来说是不可能的,因为函数没有方法:

1
  f.toString

或者我们可以通过调用f上的compose方法并将两个不同的函数链接在一起来定义另一个Function1[Int,Int]对象:

1
 val f2 = f.compose((x:Int) => x - 1)

现在,如果我们要实际执行函数,或者像数学家所说的"将函数应用于其参数",我们将在Function1[Int,Int]对象上调用apply方法:

1
 f2.apply(2)

每次要执行一个表示为对象的函数时,编写f.apply(args)都是面向对象的方法,但是如果不添加太多附加信息,就会给代码增加很多混乱,而且能够使用更多的标准符号(如f(args))也会很好。这就是scala编译器介入的地方,每当我们引用f到一个函数对象,并编写f (args)来将参数应用到表示的函数时,编译器就会悄悄地将f (args)扩展到对象方法调用f.apply (args)

scala中的每一个函数都可以作为一个对象来处理,它也可以以另一种方式工作——只要每个对象具有apply方法,就可以作为一个函数来处理。这些对象可以在函数符号中使用:

1
2
3
4
5
6
7
8
// we will be able to use this object as a function, as well as an object
object Foo {
  var y = 5
  def apply (x: Int) = x + y
}


Foo (1) // using Foo object in function notation

当我们希望将对象视为函数时,有许多使用案例。最常见的场景是工厂模式。我们不必使用工厂方法向代码中添加混乱,而是可以将cx1〔0〕对象添加到一组参数中,以创建关联类的新实例:

1
2
3
4
5
List(1,2,3) // same as List.apply(1,2,3) but less clutter, functional notation

// the way the factory method invocation would have looked
// in other languages with OO notation - needless clutter
List.instanceOf(1,2,3)

因此,apply方法只是缩小scala中函数和对象之间差距的一种简便方法。


它来自于这样一个想法,即你经常想把一些东西应用到一个对象上。更准确的例子是一家工厂。当您有一个工厂时,您需要对它应用参数来创建一个对象。

scala的人认为,在很多情况下,有一个快捷方式调用apply可能会更好。因此,当您直接向一个对象提供参数时,它会被丢弃,就像您将这些参数传递给该对象的apply函数一样:

1
2
3
4
5
6
class MyAdder(x: Int) {
  def apply(y: Int) = x + y
}

val adder = new MyAdder(2)
val result = adder(4) // equivalent to x.apply(4)

它通常用于伴生对象中,为类或特性提供一个好的工厂方法,下面是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
trait A {
  val x: Int
  def myComplexStrategy: Int
}

object A {
  def apply(x: Int): A = new MyA(x)

  private class MyA(val x: Int) extends A {
    val myComplexStrategy = 42
  }
}

从scala标准库中,您可以看到scala.collection.Seq是如何实现的:Seq是一个特性,因此new Seq(1, 2)不会编译,但由于伴随对象和应用,您可以调用Seq(1, 2),实现是由伴随对象选择的。


下面是一个小例子,给那些想快速阅读的人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 object ApplyExample01 extends App {


  class Greeter1(var message: String) {
    println("A greeter-1 is being instantiated with message" + message)


  }

  class Greeter2 {


    def apply(message: String) = {
      println("A greeter-2 is being instantiated with message" + message)
    }
  }

  val g1: Greeter1 = new Greeter1("hello")
  val g2: Greeter2 = new Greeter2()

  g2("world")


}

output

A greeter-1 is being instantiated with message hello

A greeter-2 is being instantiated with message world