Das hier verwendete Scala-Konstrukt ist die Extractor. Das Symbol ::
ist nichts anderes als eine Fallklasse in Scala, wo eine unapply
-Methode auf ihrem Begleitobjekt existiert, um die Extraktionsmagie zu ermöglichen. Here ist eine gute eingehende Anleitung zu Extraktoren. Aber hier ist die Zusammenfassung:
Immer wenn Sie den Inhalt einer Klasse, entweder für variable Bindung oder als Teil des Mustervergleichs "entpacken" wollen, sucht der Compiler nach der Methode unapply
auf welches Symbol auf der linken Seite ist des Ausdrucks. Dies kann ein Objekt, ein Fallklassen-Companion-Objekt (wie ::
, in Ihrer Frage) oder eine Instanz mit einem unapply
sein. Das Argument zu unapply
ist der zu entpackende eingehende Typ, und der Rückgabetyp ist eine Option
von dem, was als die erwartete Struktur und die Typen deklariert wurde. Im Mustervergleich zeigt eine None
an, dass eine Übereinstimmung nicht gefunden wurde. Bei variabler Bindung wird MatchError
ausgelöst, wenn None
das Ergebnis ist.
Eine gute Denkweise über unapply
ist, dass es das Gegenteil von apply
ist. Wobei unapply
der Empfänger der Funktionsaufrufsyntax unapply
der Empfänger von Extraktoraufrufen ist.
dies weiter zu veranschaulichen, wollen wir einen einfachen Fall Klasse definieren:
case class Cat(name: String, age: Int)
Weil es ein Fall der Klasse ist, bekommen wir apply
und unapply
Methoden auf dem Begleitobjekt automatisch erzeugt, die wie folgt aussehen grob:
object Cat {
// compiler generated...
def apply(name: String, age: Int) = new Cat(name, age)
def unapply(aCat: Cat): Option[(String, Int)] = Some((aCat.name, aCat.age))
}
Wenn Sie eine Cat
über das Companion-Objekt erstellen, wird apply
aufgerufen. Wenn Sie die Bestandteile eines Cat
auspacken, wird unapply
genannt:
val mycat = Cat("freddy", 3) // `apply` called here
...
val Cat(name, age) = mycat // `unapply` called here
...
val animal: AnyRef = mycat
val info = animal match {
case Cat(name, age) => "My pet " + name // `unapply` called here
case _ => "Not my pet"
}
// info: String = My pet freddy
Da unapply
kehrt ein Option
wir viel Kraft haben Extraktoren zu schreiben, die interessantere Fälle behandeln, zum Beispiel testen, ob ein ankommender Typ entspricht vor dem Extrahieren von Werten einigen Kriterien. Nehmen wir zum Beispiel an, wir möchten den Namen von Katzen bekommen, die "alt" sind.Man könnte dies tun:
object OldCatName {
def unapply(aCat: Cat) = if (aCat.age >= 10) Some(aCat.name) else None
}
Verwendung das gleiche wie ein erzeugt unapply
wäre:
val yourcat = Cat("betty", 12)
...
val OldCatName(name1) = yourcat
// name1: String = "betty"
val OldCatName(name2) = mycat
// scala.MatchError: Cat(freddy,3) (of class Cat)
MatchError
s nicht eine schöne Sache zu ermöglichen, also lassen Sie uns Musterabgleich verwenden:
val conditions = Seq(mycat, yourcat) map {
case OldCatName(oldie) => s"$oldie is old"
case Cat(name, age) => s"At age $age $name is not old"
}
// conditions: Seq[String] = List(At age 3 freddy is not old, betty is old)
Das eine zusätzliche bisschen Magie beteiligt mit der unapply
Methode für ::
ist, dass einige syntaktische Zuckerermöglichtzu schreiben val head :: tail = ...
stattdessen.
http://blog.lunatech.com/2011/11/25/scala-list-extract-demystified gute Erklärung, wie es funktioniert –
@ eugene-zhulenev, danke für den Link. Das war genau richtig. Die Cons-Fallklasse bietet automatisch die Möglichkeit, eine List-Instanz sowohl zu konstruieren als auch zu einem Kopf und einem Schwanz zu dekonstruieren. – poissondist
@poissondist der Link von Eugene Zhulenev ist gebrochen. Was denkst du über meine Antwort unten? – metasim