Je n’ai jamais compris cela à partir des noms inventés et vérifiant (une classe AddTwo
a une apply
qui ajoute deux!) Exemples.
Je comprends que c'est un sucre syntaxique, donc (je déduis du contexte) il doit avoir été conçu pour rendre un code plus intuitif.
Quel sens une classe avec une fonction apply
donne-t-elle? À quoi est-il utilisé et à quelles fins améliore-t-il le code (suppression du nom, vérification des noms, etc.)?
comment cela aide-t-il lorsqu'il est utilisé dans un objet compagnon?
Les mathématiciens ont leurs propres petites manières amusantes, donc au lieu de dire "nous appelons alors fonction f
en le passant x
en tant que paramètre", comme diraient les programmeurs, ils parlent de "appliquer la fonction f
à son argument x
".
En mathématiques et en informatique, Apply est une fonction qui applique des fonctions à des arguments.
Wikipedia
apply
a pour but de réduire l'écart entre les paradigmes fonctionnel et orienté objet de Scala. Chaque fonction de Scala peut être représentée sous forme d'objet. Chaque fonction a également un type OO: par exemple, une fonction qui prend un paramètre Int
et renvoie un Int
aura le type 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
Puisque tout est un objet dans Scala, _ f
peut maintenant être traité comme une référence à Function1[Int,Int]
objet. Par exemple, nous pouvons appeler la méthode toString
héritée de Any
, ce qui aurait été impossible pour une fonction pure, car les fonctions n'ont pas de méthodes:
f.toString
Ou nous pourrions définir un autre objet Function1[Int,Int]
en appelant la méthode compose
sur f
et en chaînant deux fonctions différentes:
val f2 = f.compose((x:Int) => x - 1)
Maintenant, si nous voulons réellement exécuter la fonction, ou comme le dit un mathématicien "appliquer une fonction à ses arguments", nous appellerions la méthode apply
sur l'objet Function1[Int,Int]
:
f2.apply(2)
Écrire f.apply(args)
chaque fois que vous souhaitez exécuter une fonction représentée sous la forme d'un objet correspond à la méthode orientée objet, mais ajouterait beaucoup de fouillis au code sans ajouter beaucoup d'informations supplémentaires et il serait agréable de pouvoir l'utiliser. notation plus standard, telle que f(args)
. C'est là que le compilateur Scala intervient et chaque fois que nous avons une référence f
à un objet fonction et écrivez f (args)
pour appliquer des arguments à la fonction représentée, le compilateur étend silencieusement f (args)
à l'appel de méthode d'objet f.apply (args)
.
Chaque fonction de Scala peut être traitée comme un objet et fonctionne dans l'autre sens: chaque objet peut être traité comme une fonction, à condition qu'il utilise la méthode apply
. De tels objets peuvent être utilisés dans la notation de fonction:
// 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
Il existe de nombreux cas d'utilisation lorsque l'on souhaite traiter un objet comme une fonction. Le scénario le plus courant est un modèle d'usine . Au lieu d'ajouter du fouillis au code en utilisant une méthode de fabrique, nous pouvons apply
objecter à un ensemble d'arguments pour créer une nouvelle instance d'une classe associée:
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)
Donc, la méthode apply
n'est qu'un moyen pratique de combler le fossé entre les fonctions et les objets dans Scala.
Cela vient de l'idée que vous voulez souvent appliquer quelque chose à un objet. L'exemple le plus précis est celui des usines. Quand vous avez une usine, vous voulez appliquer paramètre pour créer un objet.
Les gars de Scala pensaient que, comme cela se produit souvent, il pourrait être agréable d’avoir un raccourci pour appeler apply
. Ainsi, lorsque vous donnez des paramètres directement à un objet, il est préférable de passer ces paramètres à la fonction apply de cet objet:
class MyAdder(x: Int) {
def apply(y: Int) = x + y
}
val adder = new MyAdder(2)
val result = adder(4) // equivalent to x.apply(4)
Il est souvent utilisé dans un objet compagnon, pour fournir une méthode de fabrique Nice pour une classe ou un trait, en voici un exemple:
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
}
}
À partir de la bibliothèque standard scala, vous pouvez voir comment scala.collection.Seq
est implémenté: Seq
est un trait, donc new Seq(1, 2)
ne sera pas compilé mais grâce à l'objet compagnon et appliquer, vous pouvez appeler Seq(1, 2)
et l'implémentation est choisie par l'objet compagnon.
Voici un petit exemple pour ceux qui veulent parcourir rapidement
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")
}
sortie
Un greeter-1 est en cours d'instanciation avec le message hello
Un greeter-2 est en cours d'instanciation avec le monde des messages