Ich habe das Scala playframework Tutorial durchgearbeitet und bin auf dieses Codefragment gestoßen, das mich verwirrt hat:
def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
errors => BadRequest(views.html.index(Task.all(), errors)),
label => {
Task.create(label)
Redirect(routes.Application.tasks())
}
)
}
Also habe ich mich entschlossen zu recherchieren und bin auf diesen Beitrag gestoßen.
Ich verstehe es immer noch nicht.
Was ist der Unterschied zwischen diesen:
implicit def double2Int(d : Double) : Int = d.toInt
und
def double2IntNonImplicit(d : Double) : Int = d.toInt
abgesehen von der offensichtlichen Tatsache haben sie unterschiedliche Methodennamen.
Wann sollte ich implicit
verwenden und warum?
Im Folgenden werden die wichtigsten Anwendungsfälle von Implikiten erläutert. Weitere Informationen finden Sie im relevantes Kapitel der Programmierung in Scala .
Implizite Parameter
Die letzte Parameterliste einer Methode kann mit implicit
markiert werden, dh die Werte werden aus dem Kontext entnommen, in dem sie aufgerufen werden. Wenn der Gültigkeitsbereich keinen impliziten Wert des richtigen Typs enthält, wird er nicht kompiliert. Da der implizite Wert in einen einzelnen Wert aufgelöst werden muss und Konflikte vermieden werden sollen, ist es eine gute Idee, den Typ spezifisch für seinen Zweck zu machen, z. Benötigen Sie Ihre Methoden nicht, um ein implizites Int
zu finden!
beispiel:
// probably in a library
class Prefixer(val prefix: String)
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s
// then probably in your application
implicit val myImplicitPrefixer = new Prefixer("***")
addPrefix("abc") // returns "***abc"
Implizite Konvertierungen
Wenn der Compiler einen Ausdruck mit dem falschen Typ für den Kontext findet, sucht er nach einem impliziten Wert Function
eines Typs, mit dem er die Typprüfung durchführen kann. Wenn also ein A
erforderlich ist und ein B
gefunden wird, wird nach einem impliziten Wert vom Typ B => A
Im Gültigkeitsbereich gesucht (es werden auch einige andere Stellen wie in B
und A
Begleitobjekte (falls vorhanden). Da def
s zu Function
- Objekten "eta-expandiert" werden können, kann auch eine implicit def xyz(arg: B): A
verwendet werden.
Der Unterschied zwischen Ihren Methoden besteht also darin, dass der Compiler die mit implicit
markierte Methode für Sie einfügt, wenn eine Double
gefunden wird, aber eine Int
erforderlich ist.
implicit def doubleToInt(d: Double) = d.toInt
val x: Int = 42.0
wird genauso funktionieren wie
def doubleToInt(d: Double) = d.toInt
val x: Int = doubleToInt(42.0)
In der zweiten haben wir die Konvertierung manuell eingefügt; im ersten hat der compiler das selbe automatisch gemacht. Die Konvertierung ist aufgrund der Typanmerkung auf der linken Seite erforderlich.
Bezüglich Ihres ersten Snippets aus dem Spiel:
Aktionen werden auf diese Seite in der Play-Dokumentation erklärt (siehe auch API-Dokumente ). Du benutzt
apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent]
auf dem Objekt Action
(das der Begleiter des gleichnamigen Merkmals ist).
Wir müssen also eine Funktion als Argument angeben, die als Literal in das Formular geschrieben werden kann
request => ...
In einem Funktionsliteral ist der Teil vor dem =>
Eine Wertdeklaration und kann wie in jeder anderen implicit
- Deklaration mit val
markiert werden. Hier muss request
nicht mit implicit
markiert sein, damit check eingegeben werden kann. Dadurch ist es jedoch als verfügbar impliziter Wert für alle Methoden, die ihn innerhalb der Funktion benötigen könnten (und natürlich auch explizit verwendet werden können). In diesem speziellen Fall wurde dies durchgeführt, weil für die Methode bindFromRequest
in der Klasse Form ein implizites Argument Request
erforderlich ist.
ACHTUNG: enthält Sarkasmus mit Bedacht! YMMV ...
Luigis Antwort ist vollständig und richtig. Dies ist nur, um es ein wenig mit einem Beispiel zu erweitern, wie Sie herrlich überbeanspruchen können implicits, wie es ziemlich oft in Scala) Projekten vorkommt. Eigentlich so oft , Sie können es wahrscheinlich sogar in einer der "Best Practice" Anleitungen finden.
object HelloWorld {
case class Text(content: String)
case class Prefix(text: String)
implicit def String2Text(content: String)(implicit prefix: Prefix) = {
Text(prefix.text + " " + content)
}
def printText(text: Text): Unit = {
println(text.content)
}
def main(args: Array[String]): Unit = {
printText("World!")
}
// Best to hide this line somewhere below a pile of completely unrelated code.
// Better yet, import its package from another distant place.
implicit val prefixLOL = Prefix("Hello")
}
Warum und wann sollten Sie den Parameter request
als implicit
markieren:
Einige Methoden, die Sie im Hauptteil Ihrer Aktion verwenden, haben eine implizite Parameterliste, wie z. B. Form.scala eine Methode definiert:
def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }
Sie bemerken dies nicht unbedingt, da Sie nur myForm.bindFromRequest()
aufrufen würden. Sie müssen die impliziten Argumente nicht explizit angeben. Nein, Sie überlassen es dem Compiler , bei jedem Methodenaufruf, der eine Instanz der Anforderung erfordert, nach einem gültigen Kandidatenobjekt zu suchen, das übergeben werden soll. Da Ihnen eine Anfrage zur Verfügung steht , müssen Sie sie nur als implicit
markieren.
Sie markieren es explizit als verfügbar für implizite Verwendung.
Sie weisen den Compiler an, dass es "OK" ist, das vom Play-Framework gesendete Anforderungsobjekt zu verwenden (wir haben den Namen "request" angegeben, hätten aber auch nur "r" oder "req" verwenden können), wo immer dies erforderlich ist, "on the sly". .
myForm.bindFromRequest()
siehst du es? es ist nicht da, aber es ist da!
Es passiert einfach, ohne dass Sie es manuell an jeder Stelle einfügen müssen, die es benötigt (aber Sie können es explizit weitergeben, wenn Sie dies wünschen, egal ob es ist mit implicit
markiert oder nicht):
myForm.bindFromRequest()(request)
Ohne es als implizit zu markieren, würden Sie müssen das oben Genannte tun. Wenn Sie es als implizit markieren, müssen Sie es nicht.
Wann sollten Sie die Anfrage als implicit
markieren? Dies ist nur dann wirklich erforderlich, wenn Sie Methoden verwenden, die eine implizite Parameterliste deklarieren, die eine Instanz der Anforderung erwartet . Aber um es einfach zu halten, könnten Sie es sich zur Gewohnheit machen, die Anfrage implicit
immer zu markieren. Auf diese Weise können Sie einfach schönen, knappen Code schreiben.
Im obigen Fall sollte es auch eine implizite Funktion only one
Geben, deren Typ double => Int
Ist. Andernfalls wird der Compiler verwirrt und nicht richtig kompiliert.
//this won't compile
implicit def doubleToInt(d: Double) = d.toInt
implicit def doubleToIntSecond(d: Double) = d.toInt
val x: Int = 42.0
In scala implizit funktioniert als:
Konverter
Parameterwert Injektor
Es gibt 3 Arten der Verwendung von Implicit
Implizit Typkonvertierung: Konvertiert die fehlererzeugende Zuweisung in den vorgesehenen Typ
val x: String = "1"
wert y: Int = x
Stringist nicht der -Untertyp von Int, daher tritt der Fehler in Zeile 2 auf. Um den Fehler zu beheben, sucht der Compiler im Gültigkeitsbereich nach einer solchen Methode, die ein implizites Schlüsselwort enthält, und verwendet Stringals Argument und gibt ein Intzurück.
damit
implicit def z(a:String):Int = 2
val x :String = "1"
val y:Int = x // compiler will use z here like val y:Int=z(x)
println(y) // result 2 & no error!
Implizit Empfängerumwandlung: Wir rufen im Allgemeinen die Eigenschaften eines Empfängerobjekts auf, z. B. Methoden oder Variablen. Um also eine Eigenschaft eines Empfängers aufzurufen, muss die Eigenschaft Mitglied der Klasse/des Objekts dieses Empfängers sein.
class Mahadi{
val haveCar:String ="BMW"
}
class Johnny{
val haveTv:String = "Sony"
}
val mahadi = new Mahadi
mahadi.haveTv // Error happening
Hier wird mahadi.haveTveinen Fehler erzeugen. Denn scala Der Compiler sucht zuerst nach der Eigenschaft haveTvfür mahadireceiver. Wird nicht gefunden. Zweitens Es wird nach einer Methode im Gültigkeitsbereich mit implizites Schlüsselwortgesucht, die Mahadi-Objektals Argument verwendet und Johnny-Objektzurückgibt. Dies ist jedoch nicht der Fall. Daher wird FehlerAber das Folgende ist in Ordnung.
class Mahadi{
val haveCar:String ="BMW"
}
class Johnny{
val haveTv:String = "Sony"
}
val mahadi = new Mahadi
implicit def z(a:Mahadi):Johnny = new Johnny
mahadi.haveTv // compiler will use z here like new Johnny().haveTv
println(mahadi.haveTv)// result Sony & no error
Implizit Parameterinjektion: Wenn wir eine Methode aufrufen und ihren Parameterwert nicht übergeben, verursacht dies einen Fehler. Der scala= Compiler funktioniert wie folgt - zuerst wird versucht, Wert zu übergeben, es wird jedoch kein direkter Wert für den Parameter erhalten.
def x(a:Int)= a
x // ERROR happening
Zweitens, wenn der Parameter ein implizites Schlüsselwort hat, sucht er nach einem valim scope, das den gleichen Typvon Wert hat. Wenn nicht, wird er einen Fehler verursachen.
def x(implicit a:Int)= a
x // error happening here
Um dieses Problem zu lösen, sucht der Compiler nach einem impliziten Wertmit dem Typ von Int, da der Parameter aimplizites Schlüsselworthat.
def x(implicit a:Int)=a
implicit val z:Int =10
x // compiler will use implicit like this x(z)
println(x) // will result 10 & no error.
Ein weiteres Beispiel:
def l(implicit b:Int)
def x(implicit a:Int)= l(a)
wir können es auch so schreiben
def x(implicit a:Int)= l
Da leinen impliziten Parameterund im Gültigkeitsbereich von bodyder Methode x) einen impliziten lokalen Variablen( Parameter sind lokale Variablen ) adas ist der Parameter von x, also im Hauptteil von xmethoddie Methodensignatur das implizite Argument von l) valuewird durch die lokale implizite Variable (Parameter) a
der Methode x) implizitabgelegt.
Damit
def x(implicit a:Int)= l
wird so in Compiler sein
def x(implicit a:Int)= l(a)
Ein weiteres Beispiel:
def c(implicit k:Int):String = k.toString
def x(a:Int => String):String =a
x{
x => c
}
es wird Fehler verursachen, weil cin x {x => c}explizit die Übergabe von Werten in Argumenten oder impliziten Werten erfordert in scope.
Also können wir den Parameterexplizit implizitbeim Aufruf der Methode x
x{
implicit x => c // the compiler will set the parameter of c like this c(x)
}
Dies wurde in action methodvon Play-Framework verwendet
def index = Action{
implicit request =>
Ok(views.html.formpage)
}