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


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow