Scala中的apply函数是什么?

时间:2012-03-16 12:36:39

标签: scala

我从来没有从设计的解组和修饰名词中理解它(AddTwo类有一个apply加上两个!)的例子。

我理解它是语法糖,所以(我从上下文推断)它必须设计成使一些代码更直观。

具有apply功能的班级有什么意义?它用于什么,以及它使代码变得更好的目的(解组,修改名词等)?

在伴侣对象中使用它有何帮助?

5 个答案:

答案 0 :(得分:581)

数学家有他们自己的一些有趣的方式,所以不要说“然后我们称函数fx作为参数传递”,正如我们程序员所说,他们谈论“应用函数{{ 1}}到它的参数f“。

  

在数学和计算机科学中,Apply是一个适用的函数   参数的功能。
   Wikipedia

x旨在缩小Scala中面向对象和功能范例之间的差距。 Scala中的每个函数都可以表示为一个对象。每个函数也有一个OO类型:例如,一个带有apply参数并返回Int的函数的OO类型为Int

Function1[Int,Int]

由于Scala // define a function in scala (x:Int) => x + 1 // assign an object representing the function to a variable val f = (x:Int) => x + 1 中的所有内容都是对象,因此现在可以将其视为对f对象的引用。例如,我们可以调用从Function1[Int,Int]继承的toString方法,这对于纯函数来说是不可能的,因为函数没有方法:

Any

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

f

现在,如果我们想要实际执行该函数,或者数学家说“将函数应用于其参数”,我们将在 val f2 = f.compose((x:Int) => x - 1) 对象上调用apply方法:

Function1[Int,Int]

每次要执行表示为对象的函数时,编写 f2.apply(2) 都是面向对象的方式,但是会在不添加太多额外信息的情况下为代码添加大量混乱,这样会很好能够使用更标准的表示法,例如f.apply(args)。这就是Scala编译器的步骤,每当我们对函数对象有一个引用f(args)并编写f以将参数应用于所表示的函数时,编译器会静默地将f (args)扩展为对象方法调用{ {1}}。

Scala中的每个函数都可以被视为一个对象,它也可以用于其他方式 - 只要它具有f (args)方法,每个对象都可以被视为一个函数。这些对象可以在函数表示法中使用:

f.apply (args)

当我们想要将对象视为函数时,有许多用例。最常见的情况是factory pattern。我们可以apply反对一组参数来创建关联类的新实例,而不是使用工厂方法向代码添加混乱:

// 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 

所以apply方法只是缩小Scala中函数和对象之间差距的一种方便方法。

答案 1 :(得分:44)

它来自于您经常想要某些东西应用于对象的想法。更准确的例子是工厂之一。当您有工厂时,您希望参数应用于它以创建对象。

斯卡拉家伙认为,在许多情况下都会发生这种情况,有一个快捷方式可以调用apply。因此,当您直接向对象提供参数时,它就像将这些参数传递给该对象的apply函数一样令人厌恶:

class MyAdder(x: Int) {
  def apply(y: Int) = x + y
}

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

它通常用于伴侣对象,为类或特性提供一个很好的工厂方法,这是一个例子:

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),并由配对对象选择实现。

答案 2 :(得分:7)

对于那些想要快速阅读的人来说,这是一个小例子

 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")


} 
  

输出

     

正在使用消息hello

实例化greeter-1      

greeter-2正在使用消息世界进行实例化

答案 3 :(得分:0)

来自c++的人的 TLDR

它只是 ( ) 括号的重载运算符

所以在 Scala 中:

class X {
   def apply(param1: Int, param2: Int, param3: Int) : Int = {
     // Do something
   }
}

在c++中与此相同:

class X {
   int operator()(int param1, int param2, int param3) {
      // do something
   }
};

答案 4 :(得分:0)

1 - 将函数视为对象。

2 - apply 方法类似于 Python 中的 __call __,它允许您将给定类的实例用作函数。