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 selection
applicabile args
(anche dopo aver provato a convertire args
con 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.prefix
a un tipo con selection
metodi 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.Ordering
e come viene utilizzata SeqLike#sorted
. I parametri impliciti vengono anche utilizzati per passare manifesti e CanBuildFrom
istanze 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 A
che 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 implicitly
per 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)