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()
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow