Was ist der Zweck von Typzuschreibungen in Scala?


75

In der Spezifikation gibt es nicht viele Informationen darüber, um welche Typzuordnung es sich handelt, und es gibt sicherlich nichts über den Zweck dafür. Wofür würde ich die Typzuweisung verwenden, außer "vorübergehende Varargs zum Laufen bringen"? Im Folgenden finden Sie einige Beispiele für die Syntax und die Auswirkungen der Verwendung.

scala> val s = "Dave"
s: java.lang.String = Dave

scala> val p = s:Object
p: java.lang.Object = Dave

scala> p.length
<console>:7: error: value length is not a member of java.lang.Object
       p.length
         ^
scala> p.getClass
res10: java.lang.Class[_ <: java.lang.Object] = class java.lang.String

scala> s.getClass
res11: java.lang.Class[_ <: java.lang.Object] = class java.lang.String

scala> p.asInstanceOf[String].length
res9: Int = 4

Antworten:


80

Die Typzuweisung teilt dem Compiler lediglich mit, welchen Typ Sie von einem Ausdruck aus allen möglichen gültigen Typen erwarten.

Ein Typ ist gültig, wenn er vorhandene Einschränkungen wie Varianz- und Typdeklarationen berücksichtigt und entweder einer der Typen ist, auf die der Ausdruck " ist a " angewendet wird , oder wenn eine Konvertierung im Gültigkeitsbereich gilt.

Also, java.lang.String extends java.lang.Objectalso ist jeder Stringauch ein Object. In Ihrem Beispiel haben Sie angegeben, dass der Ausdruck sals Objectund nicht als behandelt werden soll String. Da dies durch keine Einschränkungen verhindert wird und der gewünschte Typ einer der Typen s ist , funktioniert a.

Warum willst du das? Bedenken Sie:

scala> val s = "Dave"
s: java.lang.String = Dave

scala> val p = s: Object
p: java.lang.Object = Dave

scala> val ss = scala.collection.mutable.Set(s)
ss: scala.collection.mutable.Set[java.lang.String] = Set(Dave)

scala> val ps = scala.collection.mutable.Set(p)
ps: scala.collection.mutable.Set[java.lang.Object] = Set(Dave)

scala> ss += Nil
<console>:7: error: type mismatch;
 found   : scala.collection.immutable.Nil.type (with underlying type object Nil)
 required: java.lang.String
       ss += Nil
             ^

scala> ps += Nil
res3: ps.type = Set(List(), Dave)

Sie hätten dies auch beheben können, indem Sie sbei der ssDeklaration ascripting eingeben, oder Sie hätten den ssTyp deklarieren können Set[AnyRef].

Typdeklarationen erreichen jedoch nur dasselbe, solange Sie einem Bezeichner einen Wert zuweisen. Was man natürlich immer tun kann, wenn man den Code nicht mit One-Shot-Identifikatoren übersät. Folgendes wird beispielsweise nicht kompiliert:

def prefixesOf(s: String) = s.foldLeft(Nil) { 
  case (head :: tail, char) => (head + char) :: head :: tail
  case (lst, char) => char.toString :: lst
}

Aber das tut:

def prefixesOf(s: String) = s.foldLeft(Nil: List[String]) { 
  case (head :: tail, char) => (head + char) :: head :: tail
  case (lst, char) => char.toString :: lst
}

Es wäre dumm, hier anstelle von einen Bezeichner zu verwenden Nil. Und obwohl ich List[String]()stattdessen einfach schreiben könnte , ist das nicht immer eine Option. Betrachten Sie dies zum Beispiel:

def firstVowel(s: String) = s.foldLeft(None: Option[Char]) { 
  case (None, char) => if ("aeiou" contains char.toLower) Some(char) else None
  case (vowel, _) => vowel
}

Als Referenz ist dies, was Scala 2.7 spec (Entwurf vom 15. März 2009) über die Typzuweisung zu sagen hat:

Expr1 ::= ...
        | PostfixExpr Ascription

Ascription ::= ‘:’ InfixType
             | ‘:’ Annotation {Annotation}
             | ‘:’ ‘_’ ‘*’

28

Eine Möglichkeit ist, wenn auf Netzwerk- und serieller Protokollebene, dann dies:

val x = 2 : Byte

ist weitaus sauberer als

val x = 2.asInstanceOf[Byte]

Die zweite Form ist ebenfalls eine Laufzeitkonvertierung (nicht vom Compiler verarbeitet) und kann zu interessanten Über- / Unterlaufbedingungen führen.


1
Heh, habe etwas Neues gelernt. Ich mache eine unglückliche Menge an Netzwerkprotokoll-Sachen. Gut zu wissen!
Justin W

21
Warum nicht die alltägliche Syntax verwenden? val x: Byte = 2
Jerry101

0

Ich benutze die Typzuweisung, um über Löcher in Scalas Typinferenz zu schreiben. Zum Beispiel nimmt foldLeft über eine Sammlung vom Typ A ein Anfangselement vom Typ B und eine Funktion (B, A) => B, mit der die Elemente der Sammlung in das Anfangselement gefaltet werden. Der tatsächliche Wert vom Typ B wird aus dem Typ des Anfangselements abgeleitet. Da Nil List [Nothing] erweitert, verursacht die Verwendung als Anfangselement Probleme:

scala> val x = List(1,2,3,4)
x: List[Int] = List(1, 2, 3, 4)

scala> x.foldLeft(Nil)( (acc,elem) => elem::acc)
<console>:9: error: type mismatch;
 found   : List[Int]
 required: scala.collection.immutable.Nil.type
              x.foldLeft(Nil)( (acc,elem) => elem::acc)
                                                 ^

scala> x.foldLeft(Nil:List[Int])( (acc,elem) => elem::acc )
res2: List[Int] = List(4, 3, 2, 1)

Alternativ können Sie auch List.empty [Int] anstelle von Nil: List [Int] verwenden.

scala> x.foldLeft(List.empty[Int])( (acc,elem) => elem::acc )
res3: List[Int] = List(4, 3, 2, 1)

edit: List.empty [A] ist implementiert als

override def empty[A]: List[A] = Nil

(Quelle)

Dies ist effektiv eine ausführlichere Form von Nil: List [A]


Warum nicht x.foldLeft[List[Int]](Nil)( (acc,elem) => elem::acc )?
Jeffrey Aguilera

0

Möglicherweise ist dieser Thread aufschlussreich, wenn er etwas kompliziert ist. Es ist wichtig zu beachten, dass Sie der Typprüfung Hinweise zu Einschränkungen hinzufügen. Dadurch haben Sie ein wenig mehr Kontrolle darüber, was diese Kompilierungsphase tut.


:) Ich habe meine Frage hier erneut veröffentlicht, damit sie erhalten bleibt. Die Antwort, die ich ausgewählt habe, ist die aus dem Thread, die mir am
klarsten war

Entschuldigung, habe das gerade gesehen. Erledigt.
Justin W

0

Typinferenz: Wir können die explizite Angabe des Namens des Typs von etwas im Quellcode überspringen, der als Typinferenz bezeichnet wird (obwohl dies in einigen Ausnahmefällen erforderlich ist.)

Typzuschreibung: Die explizite Angabe des Typs von etwas wird als Typzuschreibung bezeichnet. Welchen Unterschied kann es machen?

Beispiel: Wert x = 2: Byte

Siehe auch: 1. Wir können unseren Funktionen explizit den Rückgabetyp geben

def t1 : Option[Option[String]] = Some(None)

> t1: Option[Option[String]]

Eine andere Möglichkeit, dies zu erklären, könnte sein:

def t2 = Some(None: Option[String])
> t2: Some[Option[String]]

Hier haben wir den Option[Option[String]]Rückgabetyp nicht explizit angegeben und der Compiler hat darauf geschlossen Some[Option[String]]. Warum, Some[Option[String]]weil wir in der Definition die Typzuweisung verwendet haben.

  1. Eine andere Möglichkeit, dieselbe Definition zu verwenden, ist:

    def t3 = Some(None)

    > t3: Some[None.type]

Diesmal haben wir dem Compiler nichts explizit mitgeteilt (auch nicht dieses Defi). Und es folgerte unsere Definition als Some [None.type]

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.