Suche…


Syntax

  • impliziter Wert x: T = ???

Bemerkungen

Implizite Klassen ermöglichen das Hinzufügen benutzerdefinierter Methoden zu vorhandenen Typen, ohne dass der Code geändert werden muss. Dadurch werden Typen angereichert, ohne dass der Code gesteuert werden muss.

Die Verwendung impliziter Typen zum Anreichern einer vorhandenen Klasse wird häufig als Muster zum Anreichern meiner Bibliothek bezeichnet.

Einschränkungen für implizite Klassen

  1. Implizite Klassen dürfen nur innerhalb einer anderen Klasse, eines anderen Objekts oder einer anderen Eigenschaft existieren.
  2. Implizite Klassen dürfen nur einen nicht-impliziten primären Konstruktorparameter haben.
  3. Es darf keine andere Objekt-, Klassen-, Merkmals- oder Klassenmitgliedsdefinition innerhalb desselben Bereichs geben, die denselben Namen wie die implizite Klasse hat.

Implizite Konvertierung

Bei einer impliziten Konvertierung kann der Compiler ein Objekt eines Typs automatisch in einen anderen Typ konvertieren. Dadurch kann der Code ein Objekt als Objekt eines anderen Typs behandeln.

case class Foo(i: Int)

// without the implicit
Foo(40) + 2    // compilation-error (type mismatch)

// defines how to turn a Foo into an Int
implicit def fooToInt(foo: Foo): Int = foo.i

// now the Foo is converted to Int automatically when needed
Foo(40) + 2    // 42

Die Konvertierung ist einseitig: In diesem Fall können Sie 42 nicht in Foo(42) zurück konvertieren. Dazu muss eine zweite implizite Konvertierung definiert werden:

implicit def intToFoo(i: Int): Foo = Foo(i)

Beachten Sie, dass dies der Mechanismus ist, mit dem beispielsweise ein Gleitkommawert zu einem ganzzahligen Wert hinzugefügt werden kann.

Implizite Konvertierungen sollten sparsam verwendet werden, da sie das, was geschieht, verschleiern. Es empfiehlt sich, eine explizite Konvertierung über einen Methodenaufruf zu verwenden, es sei denn, durch die Verwendung einer impliziten Konvertierung wird eine spürbare Lesbarkeit erzielt.

Es gibt keine signifikanten Auswirkungen auf die Leistung von impliziten Konvertierungen.

Scala importiert automatisch verschiedene implizite Konvertierungen in scala.Predef , einschließlich aller Konvertierungen von Java in Scala und zurück. Diese sind standardmäßig in jeder Dateikompilierung enthalten.

Implizite Parameter

Implizite Parameter können nützlich sein, wenn ein Parameter eines Typs einmal im Gültigkeitsbereich definiert und dann auf alle Funktionen angewendet werden soll, die einen Wert dieses Typs verwenden.

Ein normaler Funktionsaufruf sieht ungefähr so ​​aus:

// import the duration methods
import scala.concurrent.duration._

// a normal method:
def doLongRunningTask(timeout: FiniteDuration): Long = timeout.toMillis

val timeout = 1.second
// timeout: scala.concurrent.duration.FiniteDuration = 1 second

// to call it
doLongRunningTask(timeout) // 1000

Nehmen wir an, wir haben einige Methoden, die alle eine Timeout-Dauer haben, und wir möchten alle diese Methoden mit demselben Timeout aufrufen. Wir können das Timeout als implizite Variable definieren.

// import the duration methods
import scala.concurrent.duration._

// dummy methods that use the implicit parameter
def doLongRunningTaskA()(implicit timeout: FiniteDuration): Long = timeout.toMillis
def doLongRunningTaskB()(implicit timeout: FiniteDuration): Long = timeout.toMillis

// we define the value timeout as implicit
implicit val timeout: FiniteDuration = 1.second

// we can now call the functions without passing the timeout parameter
doLongRunningTaskA() // 1000
doLongRunningTaskB() // 1000

Das funktioniert so, dass der scalac-Compiler im Geltungsbereich nach einem Wert sucht, der als implizit markiert ist und dessen Typ mit dem des impliziten Parameters übereinstimmt . Wenn er einen findet, wendet er ihn als impliziten Parameter an.

Beachten Sie, dass dies nicht funktioniert, wenn Sie zwei oder sogar mehrere Implikationen desselben Typs im Gültigkeitsbereich definieren.

Verwenden Sie zum Anpassen der Fehlermeldung die implicitNotFound Annotation für den Typ:

@annotation.implicitNotFound(msg = "Select the proper implicit value for type M[${A}]!")
case class M[A](v: A) {}

def usage[O](implicit x: M[O]): O = x.v

//Does not work because no implicit value is present for type `M[Int]`
//usage[Int]   //Select the proper implicit value for type M[Int]!
implicit val first: M[Int] = M(1)
usage[Int]     //Works when `second` is not in scope
implicit val second: M[Int] = M(2)
//Does not work because more than one implicit values are present for the type `M[Int]`
//usage[Int]   //Select the proper implicit value for type M[Int]!

Ein Timeout ist ein üblicher Anwendungsfall, oder in Akka ist das ActorSystem (meistens) immer das gleiche, daher wird es normalerweise implizit übergeben. Ein weiterer Anwendungsfall wäre Bibliotheksdesign, am häufigsten bei FP-Bibliotheken, die auf Typenklassen (wie Skalaz , Katzen oder Verzückung ) angewiesen sind.

Die Verwendung impliziter Parameter mit Grundtypen wie Int , Long , String usw. wird im Allgemeinen als schlechte Praxis betrachtet, da dies zu Verwirrung führt und den Code weniger lesbar macht.

Implizite Klassen

Implizite Klassen ermöglichen das Hinzufügen neuer Methoden zu zuvor definierten Klassen.

Die String Klasse hat keine Methode ohne withoutVowels . Dies kann wie folgt hinzugefügt werden:

object StringUtil {
  implicit class StringEnhancer(str: String) {
    def withoutVowels: String = str.replaceAll("[aeiou]", "")
  }
}

Die implizite Klasse hat einen einzigen Konstruktorparameter ( str ) mit dem Typ, den Sie erweitern möchten ( String ), und enthält die Methode, die Sie dem Typ ( withoutVowels ) "hinzufügen" withoutVowels . Die neu definierten Methoden können jetzt direkt für den erweiterten Typ verwendet werden (wenn der erweiterte Typ im impliziten Gültigkeitsbereich ist):

import StringUtil.StringEnhancer // Brings StringEnhancer into implicit scope

println("Hello world".withoutVowels) // Hll wrld

Unter der Haube definieren implizite Klassen eine implizite Konvertierung vom erweiterten Typ in die implizite Klasse wie folgt:

implicit def toStringEnhancer(str: String): StringEnhancer = new StringEnhancer(str)

Implizite Klassen werden häufig als Value-Klassen definiert, um das Erstellen von Laufzeitobjekten und damit das Entfernen des Laufzeit-Overhead zu vermeiden:

implicit class StringEnhancer(val str: String) extends AnyVal {
    /* conversions code here */
}

Mit der obigen verbesserten Definition muss nicht jedes Mal eine neue Instanz von StringEnhancer erstellt werden, wenn die withoutVowels Methode aufgerufen wird.

Implizite Parameter mit 'implizit' auflösen

Eine implizite Parameterliste mit mehr als einem impliziten Parameter annehmen:

case class Example(p1:String, p2:String)(implicit ctx1:SomeCtx1, ctx2:SomeCtx2)

SomeCtx1 nun davon ausgegangen wird, dass eine der impliziten Instanzen nicht verfügbar ist ( SomeCtx1 ), während alle anderen erforderlichen impliziten Instanzen im Gültigkeitsbereich liegen, muss zum Erstellen einer Instanz der Klasse eine Instanz von SomeCtx1 bereitgestellt werden.

Dies kann durchgeführt werden, während die jeweils andere implizite Instanz im Gültigkeitsbereich mithilfe des implicitly Schlüsselworts beibehalten wird:

Example("something","somethingElse")(new SomeCtx1(), implicitly[SomeCtx2])

Implikationen in der REPL

Um alle implicits in-scope während einer REPL Sitzung:

scala> :implicits

Um implizite Konvertierungen Predef.scala die in Predef.scala definiert Predef.scala :

scala> :implicits -v

Wenn einer einen Ausdruck hat und die Auswirkung aller für ihn geltenden Umschreibungsregeln (einschließlich Implikationen) sehen möchte

scala> reflect.runtime.universe.reify(expr) // No quotes. reify is a macro operating directly on code.

(Beispiel:

scala> import reflect.runtime.universe._
scala> reify(Array("Alice", "Bob", "Eve").mkString(", "))
resX: Expr[String] = Expr[String](Predef.refArrayOps(Array.apply("Alice", "Bob", "Eve")(Predef.implicitly)).mkString(", "))

)



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow