Scala Language
Type Inferentie
Zoeken…
Lokaal type inferentie
Scala heeft een krachtig type-interferentiemechanisme ingebouwd in de taal. Dit mechanisme wordt 'Local Type Inference' genoemd:
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
De compiler kan het type variabelen afleiden uit de initialisatie-expressie. Op dezelfde manier kan het retourtype van methoden worden weggelaten, omdat ze equivalent zijn aan het type dat wordt geretourneerd door de methode. De bovenstaande voorbeelden zijn equivalent aan de onderstaande expliciete typeverklaringen:
val i: Int = 1 + 2
val s: String = "I am a String"
def squared(x : Int): Int = x*x
Type Inferentie en Generics
De Scala-compiler kan ook typeparameters afleiden wanneer polymorfe methoden worden aangeroepen of wanneer generieke klassen worden geïnstantieerd:
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")
De bovenstaande vorm van type-inferentie is vergelijkbaar met de Diamond Operator , geïntroduceerd in Java 7.
Beperkingen op inferentie
Er zijn scenario's waarin Scala-type-inferentie niet werkt. De compiler kan bijvoorbeeld het type methodeparameters niet afleiden:
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
De compiler kan het retourtype van recursieve methoden niet afleiden:
// 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)
Voorkomen van afleiden van niets
Gebaseerd op deze blogpost .
Stel dat u een methode als deze heeft:
def get[T]: Option[T] = ???
Wanneer u het probeert aan te roepen zonder de generieke parameter op te geven, wordt Nothing
afgeleid, wat niet erg handig is voor een daadwerkelijke implementatie (en het resultaat is niet nuttig). Met de volgende oplossing kan de NotNothing
contextbeperking het gebruik van de methode voorkomen zonder het verwachte type op te geven (in dit voorbeeld is RuntimeClass
ook uitgesloten voor ClassTags
niet Nothing
, maar RuntimeClass
wordt afgeleid):
@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]] = ???
}
Voorbeeld gebruik:
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()
}