Scala中的apply函数是什么?


311

我从未从人为设计的编组和动词名词(一个AddTwo类有一个apply添加两个!)的例子中理解了它。

我知道它是语法糖,因此(我从上下文推论得出)它必须经过精心设计,以使某些代码更直观。

具有apply函数的类给出什么意思?它的作用是什么,它的目的是使代码变得更好(解组,动词名词等)?

在同伴对象中使用时有什么帮助?


4
即使是快速浏览,也会带来很多不错的文章。这是一个:jackcoughonsoftware.blogspot.com/2009/01/…–
om-nom-nom


6
实际上它是一样的Java / C ++构造,但可以返回值和具有“应用”的名字
SES

这是一个方法,而不是一个函数。方法和函数之间的区别在Scala中非常重要。
2015年

1
细说Zoidberg“方法是可以访问类的状态就是函数” twitter.github.io/scala_school/basics.html一般适用于超过斯卡拉stackoverflow.com/questions/155609/...
DharmaTurtle

Answers:


640

数学家们有他们自己的小滑稽的方式,因此,他们谈论的是“将函数应用于其参数” ,而不是像程序员那样说“然后我们将函数作为参数f传递给函数”。xfx

在数学和计算机科学中,Apply是将函数应用于参数的函数。
维基百科

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

 // 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]对象的引用。例如,我们可以调用toString从继承的方法Any,对于纯函数来说这是不可能的,因为函数没有方法:

  f.toString

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

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

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

 f2.apply(2)

写作f.apply(args)要执行代表为对象的功能,每次都是面向对象的方式,但会增加很多杂乱的代码无需增加多少额外的信息,这将是很好能够使用更多的标准符号,如作为f(args)。这就是Scala编译器f介入的地方,每当我们有一个对函数对象的引用并进行编写f (args)以将参数应用于所表示的函数时,编译器都会默默地扩展f (args)为对象方法调用f.apply (args)

Scala中的每个函数都可以视为一个对象,并且它也可以以其他方式工作-每个对象只要具有该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使用一组自变量来创建关联类的新实例,而不是使用工厂方法在代码中添加混乱:

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中函数和对象之间差距的一种便捷方法。


1
您如何将自定义变量类型添加到对象。AFAIK这是不可能的。这是一个示例:您拥有此类class Average[YType](yZeroValue:YType)。由于对象不能接受类型参数,如何从其对象传递YType?
阿德里安

通常可以根据参数推断Scala中的类型,但是如果没有,则可以通过方括号提供。因此,当实例化Average对象时,您可以说“ val avg = new Average [Int](0)”
Angelo Genovese 2014年

4
案例类在这里值得一提。案例类可以方便地,自动地和不可见地为类的伴随对象添加applyunapply方法(以及其他方法)。因此,只用这样的一行定义一个类,就case class Car(make:String, model: String, year: Short, color: String)可以通过just:产生Car类的新对象val myCar = Car("VW","Golf",1999,"Blue")。这是速记Car.apply(...)
Kjetil S.

1
every object can be treated as a function, provided it has the apply method-现在有很多道理。
Arj


51

它来自您经常想对对象应用某些东西的想法。比较准确的例子是工厂之一。有工厂时,您想对其应用参数以创建对象。

斯卡拉(Scala)的人认为,在许多情况下都会出现这种情况,最好有一个快捷方式来打电话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)并由伴随对象选择实现。


是否可以使用隐式实现等效的apply?
jayunit100

11

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

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


} 

输出

greeter-1正在用消息hello实例化

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


2
g2的消息正确吗?这实际上是在实例化时发生的,还是实际上在实例化后发生的(即使是延迟实例化的)?
蒂姆·巴拉斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.