Ecco alcuni motivi per utilizzare il metodo deliziosamente semplice implicitly.
Per comprendere / risolvere i problemi relativi alle visualizzazioni implicite
Una vista implicita può essere attivata quando il prefisso di una selezione (ad esempio, the.prefix.selection(args)non contiene un membro selectionapplicabile args(anche dopo aver provato a convertire argscon viste implicite). In questo caso, il compilatore cerca membri impliciti, definiti localmente negli ambiti correnti o racchiusi, ereditati o importati, che sono funzioni dal tipo di quello the.prefixa un tipo con selectionmetodi impliciti definiti o equivalenti.
scala> 1.min(2) // Int doesn't have min defined, where did that come from?
res21: Int = 1
scala> implicitly[Int => { def min(i: Int): Any }]
res22: (Int) => AnyRef{def min(i: Int): Any} = <function1>
scala> res22(1) //
res23: AnyRef{def min(i: Int): Int} = 1
scala> .getClass
res24: java.lang.Class[_] = class scala.runtime.RichInt
Le viste implicite possono anche essere attivate quando un'espressione non è conforme al tipo previsto, come di seguito:
scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1
Qui il compilatore cerca questa funzione:
scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>
Accesso a un parametro implicito introdotto da un contesto associato
I parametri impliciti sono probabilmente una caratteristica più importante di Scala rispetto alle viste implicite. Supportano il modello di classe di tipo. La libreria standard lo utilizza in alcuni punti: vedere scala.Orderinge come viene utilizzata SeqLike#sorted. I parametri impliciti vengono anche utilizzati per passare manifesti e CanBuildFromistanze di array .
Scala 2.8 consente una sintassi abbreviata per i parametri impliciti, chiamati Context Bounds. In breve, un metodo con un parametro di tipo Ache richiede un parametro implicito di tipo M[A]:
def foo[A](implicit ma: M[A])
può essere riscritto come:
def foo[A: M]
Ma che senso ha passare il parametro implicito ma non nominarlo? Come può essere utile quando si implementa il metodo foo?
Spesso, non è necessario fare riferimento direttamente al parametro implicito, ma verrà tunnelato come argomento implicito verso un altro metodo chiamato. Se è necessario, puoi comunque conservare la firma del metodo terse con il Context Bound e chiamare implicitlyper materializzare il valore:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
Passaggio esplicito di un sottoinsieme di parametri impliciti
Supponiamo che tu stia chiamando un metodo che stampa piuttosto una persona, usando un approccio basato sulla classe di tipo:
trait Show[T] { def show(t: T): String }
object Show {
implicit def IntShow: Show[Int] = new Show[Int] { def show(i: Int) = i.toString }
implicit def StringShow: Show[String] = new Show[String] { def show(s: String) = s }
def ShoutyStringShow: Show[String] = new Show[String] { def show(s: String) = s.toUpperCase }
}
case class Person(name: String, age: Int)
object Person {
implicit def PersonShow(implicit si: Show[Int], ss: Show[String]): Show[Person] = new Show[Person] {
def show(p: Person) = "Person(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")"
}
}
val p = Person("bob", 25)
implicitly[Show[Person]].show(p)
E se vogliamo cambiare il modo in cui il nome viene emesso? Possiamo chiamare esplicitamente PersonShow, passare esplicitamente un'alternativa Show[String], ma vogliamo che il compilatore passi il Show[Int].
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)