347 Stimmen

Implizites in Scala verstehen

Ich war meinen Weg durch die Scala playframework Tutorial und ich stieß auf diesen Codeschnipsel, die mich verwirrt hatte:

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 beschloss ich, nachzuforschen, und stieß auf diese Stelle .

Ich verstehe es immer noch nicht.

Was ist der Unterschied zwischen:

implicit def double2Int(d : Double) : Int = d.toInt

et

def double2IntNonImplicit(d : Double) : Int = d.toInt

abgesehen von der offensichtlichen Tatsache, dass sie unterschiedliche Methodennamen haben.

Wann sollte ich die implicit und warum?

442voto

Luigi Plinge Punkte 49666

Ich werde die wichtigsten Anwendungsfälle von Implikaten weiter unten erläutern, aber weitere Einzelheiten finden Sie in der das entsprechende Kapitel von Programming in Scala .

Implizite Parameter

Die endgültige Parameterliste einer Methode kann markiert werden implicit , d. h. die Werte werden aus dem Kontext entnommen, in dem sie aufgerufen werden. Wenn kein impliziter Wert des richtigen Typs im Geltungsbereich vorhanden ist, wird er nicht kompiliert. Da der implizite Wert in einen einzigen Wert aufgelöst werden muss und um Konflikte zu vermeiden, ist es eine gute Idee, den Typ spezifisch für seinen Zweck zu machen, z.B. verlangen Sie von Ihren Methoden nicht, dass sie einen impliziten Int ¡!

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 des falschen Typs für den Kontext findet, sucht er nach einem impliziten Function Wert eines Typs, der eine Typüberprüfung ermöglicht. Wenn also ein A erforderlich ist, und es findet eine B sucht es nach einem impliziten Wert des Typs B => A im Geltungsbereich (es werden auch einige andere Stellen wie in der B y A Begleitobjekte, falls vorhanden). Da def s können "eta-expandiert" werden in Function Objekte, ein implicit def xyz(arg: B): A wird es auch tun.

Der Unterschied zwischen Ihren Methoden besteht also darin, dass die mit implicit wird vom Compiler für Sie eingefügt, wenn eine Double gefunden wird, sondern ein 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)

Im zweiten Fall haben wir die Konvertierung manuell eingefügt; im ersten Fall hat der Compiler dies automatisch getan. Die Umwandlung ist aufgrund der Typ-Anmerkung auf der linken Seite erforderlich.


Zu Ihrem ersten Ausschnitt aus Play:

Die Aktionen werden erläutert unter 本ページ aus der Play-Dokumentation (siehe auch API-Dokumente ). Sie verwenden

apply(block: (Request[AnyContent])  Result): Action[AnyContent]

über die Action Objekt (das das Pendant zum gleichnamigen Trait ist).

Wir müssen also eine Funktion als Argument angeben, die als Literal in der Form geschrieben werden kann

request => ...

In einem Funktionsliteral ist der Teil vor dem => ist eine Wertdeklaration und kann markiert werden implicit wenn Sie wollen, genau wie in jedem anderen val Erklärung. Hier, request nicht müssen gekennzeichnet sein implicit für die Typenprüfung, aber dadurch wird es als impliziter Wert verfügbar für alle Methoden, die es innerhalb der Funktion benötigen könnten (und natürlich kann es auch explizit verwendet werden). In diesem speziellen Fall wurde dies getan, weil die bindFromRequest Methode auf der Formular Klasse erfordert eine implizite Request Argument.

50voto

Daniel Dinnyes Punkte 4558

WARNUNG: enthält Sarkasmus mit Bedacht! YMMV...

Luigi's Antwort vollständig und korrekt ist. Dies ist nur ein Beispiel dafür, wie man es ein wenig erweitern kann, um zu zeigen, wie man es glorreich übertreiben kann Implikationen wie es in Scala-Projekten recht häufig vorkommt. Sogar so oft, dass man es wahrscheinlich sogar in einem der "Bewährte Verfahren" Führer.

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

47voto

John Punkte 2404

In Scala funktioniert implizit als :

Konverter

Parameterwert Injektor

Methode der Erweiterung

Es gibt einige Verwendungen von Implicit

  1. Implizite Typkonvertierung : Er wandelt die fehlerhafte Zuweisung in den vorgesehenen Typ um

    val x :String = "1"
    
    val y:Int = x

Zeichenfolge ist nicht die Subtyp von Int , so dass in Zeile 2 ein Fehler auftritt. Um den Fehler zu beheben, sucht der Compiler nach einer solchen Methode im Anwendungsbereich, die ein implizites Schlüsselwort hat und eine Zeichenfolge als Argument und gibt ein Int .

also

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!
  1. Implizite Empfängerkonvertierung : Im Allgemeinen rufen wir mit dem Empfänger die Eigenschaften des Objekts auf, z.B. Methoden oder Variablen. Um also eine Eigenschaft durch einen Empfänger aufzurufen, muss die Eigenschaft ein Mitglied der Klasse/des Objekts des Empfängers sein.

     class Mahadi{
    
     val haveCar:String ="BMW"
    
     }

    class Johnny{

    val haveTv:String = "Sony"

    }

   val mahadi = new Mahadi

   mahadi.haveTv // Error happening

Hier mahadi.haveTv wird einen Fehler erzeugen. Weil der Scala-Compiler zuerst nach dem haveTv Eigenschaft zu mahadi Empfänger. Er wird nicht gefunden. Zweitens wird nach einer Methode im Geltungsbereich gesucht, die implizites Schlüsselwort die dauern Mahadi Objekt als Argument und gibt Johnny Objekt . Aber das ist hier nicht der Fall. Also wird es schaffen Fehler . Aber 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
  1. Implizite Parameterinjektion : Wenn wir eine Methode aufrufen und ihren Parameterwert nicht übergeben, führt dies zu einem Fehler. Der Scala-Compiler arbeitet so - er versucht zunächst, einen Wert zu übergeben, aber er erhält keinen direkten Wert für den Parameter.

     def x(a:Int)= a
    
     x // ERROR happening

Zweitens: Wenn der Parameter ein implizites Schlüsselwort enthält, wird nach einem val で、その Umfang die die gleiche Art von Wert. Wenn nicht, wird es einen Fehler verursachen.

def x(implicit a:Int)= a

x // error happening here

Um dieses Problem zu lösen, sucht der Compiler nach einem impliziter Wert mit der Typ von Int weil der Parameter a hat implizites Schlüsselwort .

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)

können wir es auch so schreiben.

def x(implicit a:Int)= l

Denn l hat eine impliziter Parameter und im Umfang von methode x's körper gibt es eine implizite lokale Variable ( Parameter sind lokale Variablen ) a die der Parameter von x also im Körper von x Methode die Methodensignatur l's impliziter Argumentwert wird von der die lokale implizite Variable (Parameter) der Methode x a implizit .

Also

 def x(implicit a:Int)= l

wird im Compiler wie folgt aussehen

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
}

wird es zu einem Fehler führen, weil c en x{x=>c} benötigt explizite Wertübergabe im Argument oder implizite Wertübergabe in Reichweite .

Wir können also die Funktion literal's Parameter ausdrücklich implizit wenn wir die Verfahren x

x{
implicit x => c // the compiler will set the parameter of c like this c(x)
}

Dies wurde verwendet in Aktionsmethode von Play-Framework

in view folder of app the template is declared like
@()(implicit requestHreader:RequestHeader)

in controller action is like

def index = Action{
implicit request =>

Ok(views.html.formpage())  

}

wenn Sie den Anfrageparameter nicht explizit als implizit erwähnen, dann müssen Sie geschrieben worden sein-

def index = Action{
request =>

Ok(views.html.formpage()(request))  

}
  1. Erweiterungsmethode

Denken Sie, wir wollen eine neue Methode mit Integer-Objekt hinzufügen. Der Name der Methode wird meterToCm sein,

> 1 .meterToCm 
res0 100 

Dazu müssen wir eine implizite Klasse innerhalb eines Objekts/Klasse/Eigenschaft erstellen. Diese Klasse darf keine Fallklasse sein.

object Extensions{

    implicit class MeterToCm(meter:Int){

        def  meterToCm={
             meter*100
        }

    }

}

Beachten Sie die implizite Klasse dauert nur ein Konstruktorparameter .

Importieren Sie nun die implizite Klasse in dem Bereich, den Sie verwenden möchten

import  Extensions._

2.meterToCm // result 200

7voto

Peter Perháč Punkte 20034

Warum und wann sollten Sie die request Parameter als implicit :

Einige Methoden, die Sie im Hauptteil Ihrer Aktion verwenden werden, haben eine implizite Parameterliste wie z.B. Form.scala definiert eine Methode:

def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }

Sie bemerken dies nicht unbedingt, da Sie einfach die myForm.bindFromRequest() Sie müssen die impliziten Argumente nicht explizit angeben. Nein, Sie lassen der Compiler nach einem gültigen Kandidatenobjekt zu suchen, das bei jedem Methodenaufruf, der eine Instanz der Anforderung erfordert, übergeben werden kann. Da Sie tun eine Anfrage zur Verfügung haben, müssen Sie diese lediglich als implicit .

Sie ausdrücklich markieren Sie es als verfügbar für implizit verwenden.

Sie weisen den Compiler darauf hin, dass es "OK" ist, das vom Play-Framework gesendete Request-Objekt (dem wir den Namen "request" gegeben haben, das aber auch einfach "r" oder "req" hätte heißen können) bei Bedarf "heimlich" zu verwenden.

myForm.bindFromRequest()

Sehen Sie es? Es ist nicht da, aber es ist dort!

Es geschieht einfach, ohne dass Sie es manuell an jeder Stelle einfügen müssen, an der es benötigt wird (aber Sie puede Sie können ihn ausdrücklich weitergeben, wenn Sie dies wünschen, unabhängig davon, ob er mit implicit oder nicht):

myForm.bindFromRequest()(request)

Ohne die Kennzeichnung als implizit, würden Sie müssen tun Sie das oben genannte. Wenn Sie es als implizit markieren, müssen Sie das nicht tun.

Wenn sollten Sie die Anfrage als implicit ? Das ist nur notwendig, wenn Sie Methoden verwenden, die eine implizite Parameterliste, die eine Instanz von Request erwartet . Der Einfachheit halber können Sie sich aber auch angewöhnen, die Anfrage zu markieren implicit immer . Auf diese Weise können Sie einfach schönen, knappen Code schreiben.

5voto

cozyss Punkte 1244

Außerdem sollte im obigen Fall Folgendes gelten only one implizite Funktion, deren Typ double => Int . Andernfalls kommt der Compiler durcheinander und kompiliert nicht richtig.

//this won't compile

implicit def doubleToInt(d: Double) = d.toInt
implicit def doubleToIntSecond(d: Double) = d.toInt
val x: Int = 42.0

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X