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


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow