Scala Language
Skriv inferens
Sök…
Lokal typinferens
Scala har en kraftfull typinferensmekanism inbyggd i språket. Denna mekanism benämns "Lokal typinferens":
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
Kompilatorn kan dra slutsatsen om variabler från initialiseringsuttrycket. På liknande sätt kan returtypen av metoder utelämnas, eftersom de motsvarar den typ som returneras av metodkroppen. Ovanstående exempel motsvarar nedanstående, uttryckliga typdeklarationer:
val i: Int = 1 + 2
val s: String = "I am a String"
def squared(x : Int): Int = x*x
Skriv inferens och generik
Scala-kompilatorn kan också härleda typparametrar när polymorfiska metoder kallas, eller när generiska klasser instanseras:
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")
Ovanstående form av typinferens liknar Diamond Operator , introducerad i Java 7.
Begränsningar till slutsatser
Det finns scenarier där Scala-typinferens inte fungerar. Exempelvis kan kompilatorn inte dra slutsatsen om metodparametrarna:
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
Kompilatorn kan inte dra slutsatsen om rekursiva metoder:
// 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)
Förhindra att sluta ingenting
Baserat på detta blogginlägg .
Antag att du har en metod som denna:
def get[T]: Option[T] = ???
När du försöker ringa det utan att specificera den generiska parametern blir Nothing
slutsatsen, vilket inte är särskilt användbart för en verklig implementering (och dess resultat är inte användbart). Med följande lösning kan NotNothing
kontextbundet förhindra att använda metoden utan att specificera den förväntade typen (i detta exempel är RuntimeClass
också uteslutet som för ClassTags
inte Nothing
, men RuntimeClass
sluts ut):
@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]] = ???
}
Exempel på användning:
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()
}