Scala Language
Typ Inferenz
Suche…
Lokale Typinferenz
Scala verfügt über einen leistungsfähigen, in die Sprache integrierten Typinterferenzmechanismus. Dieser Mechanismus wird als "lokale Typinferenz" bezeichnet:
val i = 1 + 2 // the type of i is Int
val s = "I am a String" // the type of s is String
def squared(x : Int) = x*x // the return type of squared is Int
Der Compiler kann den Typ der Variablen aus dem Initialisierungsausdruck ableiten. Ebenso kann der Rückgabetyp von Methoden weggelassen werden, da sie dem vom Methodenhauptteil zurückgegebenen Typ entsprechen. Die obigen Beispiele entsprechen den unten angegebenen expliziten Typdeklarationen:
val i: Int = 1 + 2
val s: String = "I am a String"
def squared(x : Int): Int = x*x
Typ Inferenz und Generics
Der Scala-Compiler kann auch Typparameter ableiten, wenn polymorphe Methoden aufgerufen werden oder wenn generische Klassen instanziiert werden:
case class InferedPair[A, B](a: A, b: B)
val pairFirstInst = InferedPair("Husband", "Wife") //type is InferedPair[String, String]
// Equivalent, with type explicitly defined
val pairSecondInst: InferedPair[String, String]
= InferedPair[String, String]("Husband", "Wife")
Die obige Form der Typeninferenz ähnelt dem in Java 7 eingeführten Diamond Operator .
Einschränkungen für Inferenz
Es gibt Szenarien, in denen die Scala-Typinferenz nicht funktioniert. Zum Beispiel kann der Compiler nicht auf die Art der Methodenparameter schließen:
def add(a, b) = a + b // Does not compile
def add(a: Int, b: Int) = a + b // Compiles
def add(a: Int, b: Int): Int = a + b // Equivalent expression, compiles
Der Compiler kann nicht auf den Rückgabetyp rekursiver Methoden schließen:
// Does not compile
def factorial(n: Int) = if (n == 0 || n == 1) 1 else n * factorial(n - 1)
// Compiles
def factorial(n: Int): Int = if (n == 0 || n == 1) 1 else n * factorial(n - 1)
Keine Schlüsse ziehen
Basierend auf diesem Blogeintrag .
Angenommen, Sie haben eine Methode wie diese:
def get[T]: Option[T] = ???
Wenn Sie versuchen, ihn ohne Angabe des generischen Parameters aufzurufen, wird Nothing
abgeleitet, was für eine tatsächliche Implementierung nicht sehr nützlich ist (und das Ergebnis ist nicht nützlich). Mit der folgenden Lösung kann der NotNothing
Kontext gebunden die Verwendung der Methode verhindern, ohne den erwarteten Typ anzugeben (in diesem Beispiel wird RuntimeClass
ebenfalls ausgeschlossen, da ClassTags
nicht Nothing
, aber RuntimeClass
wird abgeleitet):
@implicitNotFound("Nothing was inferred")
sealed trait NotNothing[-T]
object NotNothing {
implicit object notNothing extends NotNothing[Any]
//We do not want Nothing to be inferred, so make an ambigous implicit
implicit object `\n The error is because the type parameter was resolved to Nothing` extends NotNothing[Nothing]
//For classtags, RuntimeClass can also be inferred, so making that ambigous too
implicit object `\n The error is because the type parameter was resolved to RuntimeClass` extends NotNothing[RuntimeClass]
}
object ObjectStore {
//Using context bounds
def get[T: NotNothing]: Option[T] = {
???
}
def newArray[T](length: Int = 10)(implicit ct: ClassTag[T], evNotNothing: NotNothing[T]): Option[Array[T]] = ???
}
Verwendungsbeispiel:
object X {
//Fails to compile
//val nothingInferred = ObjectStore.get
val anOption = ObjectStore.get[String]
val optionalArray = ObjectStore.newArray[AnyRef]()
//Fails to compile
//val runtimeClassInferred = ObjectStore.newArray()
}