Scala Language
Tipo di inferenza
Ricerca…
Inferenza di tipo locale
Scala ha un potente meccanismo di inferenza del tipo integrato nella lingua. Questo meccanismo è definito come "Inferenza di tipo locale":
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
Il compilatore può inferire il tipo di variabili dall'espressione di inizializzazione. Allo stesso modo, il tipo di metodo restituito può essere omesso, poiché sono equivalenti al tipo restituito dal corpo del metodo. Gli esempi precedenti sono equivalenti alle seguenti, dichiarazioni di tipo esplicite:
val i: Int = 1 + 2
val s: String = "I am a String"
def squared(x : Int): Int = x*x
Tipo di inferenza e generici
Il compilatore Scala può anche dedurre parametri di tipo quando vengono chiamati metodi polimorfici o quando vengono create istanze di classi generiche:
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 precedente forma di inferenza del tipo è simile all'operatore Diamond , introdotto in Java 7.
Limitazioni all'inferenza
Esistono scenari in cui l'inferenza del tipo di Scala non funziona. Ad esempio, il compilatore non può inferire il tipo di parametri del metodo:
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
Il compilatore non può inferire il tipo di ritorno dei metodi ricorsivi:
// 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)
Prevenire il non inferire
Basato su questo post del blog .
Supponi di avere un metodo come questo:
def get[T]: Option[T] = ???
Quando si tenta di chiamarlo senza specificare il parametro generico, Nothing
viene inferito, che non è molto utile per un'implementazione effettiva (e il suo risultato non è utile). Con la seguente soluzione del NotNothing
contesto vincolato può impedire con il metodo senza specificare il tipo previsto (in questo esempio RuntimeClass
è esclusa anche da per ClassTags
non Nothing
, ma RuntimeClass
è dedotta):
@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]] = ???
}
Esempio di utilizzo:
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()
}