Scala Language
Inferencia de tipos
Buscar..
Inferencia de tipo local
Scala tiene un poderoso mecanismo de inferencia de tipos integrado al lenguaje. Este mecanismo se denomina 'Inferencia de tipo 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
El compilador puede inferir el tipo de variables de la expresión de inicialización. De manera similar, el tipo de retorno de los métodos se puede omitir, ya que son equivalentes al tipo devuelto por el cuerpo del método. Los ejemplos anteriores son equivalentes a las declaraciones de tipo explícitas a continuación:
val i: Int = 1 + 2
val s: String = "I am a String"
def squared(x : Int): Int = x*x
Tipo de Inferencia y Genéricos
El compilador Scala también puede deducir parámetros de tipo cuando se llaman métodos polimórficos, o cuando se crean instancias de clases genéricas:
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 forma anterior de inferencia de tipo es similar al Operador Diamond , introducido en Java 7.
Limitaciones a la inferencia
Hay escenarios en los que la inferencia de tipos de Scala no funciona. Por ejemplo, el compilador no puede inferir el tipo de parámetros del método:
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
El compilador no puede inferir el tipo de retorno de los métodos recursivos:
// 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)
La prevención de no inferir nada
Basado en esta entrada de blog .
Supongamos que tiene un método como este:
def get[T]: Option[T] = ???
Cuando intenta llamarlo sin especificar el parámetro genérico, Nothing
se deduce Nothing
, lo que no es muy útil para una implementación real (y su resultado no es útil). Con la siguiente solución, el NotNothing
contexto NotNothing
puede evitar el uso del método sin especificar el tipo esperado (en este ejemplo, RuntimeClass
también se excluye como para ClassTags
no Nothing
, pero RuntimeClass
se infiere):
@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]] = ???
}
Ejemplo de uso:
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()
}