Я никогда не понимал это из надуманных немаршаллирующих и глагольных существительных (класс AddTwo
имеет apply
, который добавляет два!) примера.
Я понимаю, что это синтаксический сахар, поэтому (я вывел из контекста) он должен был быть разработан, чтобы сделать некоторый код более интуитивным.
Какое значение дает класс с функцией apply
? Для чего он используется, и какие цели он делает лучше кода (unmarshalling, verbing существительные и т.д.)?
как это помогает при использовании в сопутствующем объекте?
У математиков есть свои маленькие забавные способы, поэтому вместо того, чтобы говорить "тогда мы вызываем функцию f
, передавая ее x
как параметр", как мы, программисты, говорим, они говорят о "применении функции f
к ее аргументу x
".
В математике и информатике Применить - это функция, которая применяется функции для аргументов.
Википедия
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]
, вызвав метод compose
на f
и связав две разные функции вместе:
val f2 = f.compose((x:Int) => x - 1)
Теперь, если мы хотим фактически выполнить функцию или как математик скажем "применить функцию к своим аргументам", мы бы вызвали метод apply
для объекта Function1[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
Существует много случаев использования, когда мы хотели бы рассматривать объект как функцию. Наиболее распространенным сценарием является factory pattern. Вместо добавления беспорядка в код с помощью метода factory мы можем apply
object к набору аргументов для создания нового экземпляра связанного класса:
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.
class Average[YType](yZeroValue:YType)
. Как передать YType из его объекта, поскольку объекты не могут принимать параметры типа?
Это исходит из идеи, что вы часто хотите применить что-то к объекту. Более точный пример - один из заводов. Когда у вас есть factory, вы хотите применить к нему параметр для создания объекта.
Scala ребята думали, что, как это происходит во многих ситуациях, было бы неплохо иметь ярлык для вызова 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)
Он часто используется в сопутствующем объекте, чтобы обеспечить хороший метод factory для класса или признака, вот пример:
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)
и реализация выбирается сопутствующим объектом.
Вот небольшой пример для тех, кто хочет быстро изучить
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")
}
Выход
Приветствие-1 создается с приветствием приветствия
Приветствие-2 создается с миром сообщений