Scala Language
Type Inférence
Recherche…
Inférence de type local
Scala dispose d'un puissant mécanisme d'inférence de type intégré au langage. Ce mécanisme est appelé «Inférence de type local»:
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
Le compilateur peut déduire le type de variables de l'expression d'initialisation. De même, le type de retour des méthodes peut être omis, car elles sont équivalentes au type renvoyé par le corps de la méthode. Les exemples ci-dessus sont équivalents aux déclarations de type explicites ci-dessous:
val i: Int = 1 + 2
val s: String = "I am a String"
def squared(x : Int): Int = x*x
Type Inférence Et Génériques
Le compilateur Scala peut également déduire des paramètres de type lorsque des méthodes polymorphes sont appelées ou lorsque des classes génériques sont instanciées:
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")
La forme d'inférence de type ci-dessus est similaire à l' opérateur Diamond , introduit dans Java 7.
Limites à l'inférence
Il existe des scénarios dans lesquels l'inférence de type Scala ne fonctionne pas. Par exemple, le compilateur ne peut pas déduire le type de paramètres de méthode:
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
Le compilateur ne peut pas déduire le type de retour des méthodes récursives:
// 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)
Prévenir l'inférence Rien
Basé sur cet article de blog .
Supposons que vous ayez une méthode comme celle-ci:
def get[T]: Option[T] = ???
Lorsque vous essayez de l'appeler sans spécifier le paramètre générique, Nothing
est déduit, ce qui n'est pas très utile pour une implémentation réelle (et son résultat n'est pas utile). Avec la solution suivante, le contexte NotNothing
lié peut empêcher d'utiliser la méthode sans spécifier le type attendu (dans cet exemple, RuntimeClass
est également exclu pour ClassTags
not Nothing
, mais RuntimeClass
est déduit):
@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]] = ???
}
Exemple d'utilisation:
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()
}