Oto kilka powodów, dla których warto skorzystać z cudownie prostej metody implicitly
.
Aby zrozumieć / rozwiązać problemy z widokami niejawnymi
Widok niejawny może zostać wyzwolony, gdy prefiks wyboru (rozważmy na przykład, the.prefix.selection(args)
że nie zawiera elementu członkowskiego, selection
który ma zastosowanie do args
(nawet po próbie konwersji za args
pomocą widoków niejawnych). W tym przypadku kompilator szuka niejawnych elementów członkowskich, zdefiniowanych lokalnie w bieżących lub obejmujących zakresach, dziedziczonych lub zaimportowanych, które są funkcjami z tego typu the.prefix
do typu ze selection
zdefiniowanymi lub równoważnymi metodami niejawnymi.
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
Niejawne widoki można również wyzwalać, gdy wyrażenie nie jest zgodne z oczekiwanym typem, jak poniżej:
scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1
Tutaj kompilator szuka tej funkcji:
scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>
Dostęp do niejawnego parametru wprowadzonego przez powiązanie z kontekstem
Niejawne parametry są prawdopodobnie ważniejszą cechą Scali niż niejawne widoki. Obsługują wzorzec klasy typu. Biblioteka standardowa używa tego w kilku miejscach - zobacz scala.Ordering
i jak jest używany w SeqLike#sorted
. Niejawne parametry są również używane do przekazywania manifestów tablicowych i CanBuildFrom
wystąpień.
Scala 2.8 pozwala na skróconą składnię niejawnych parametrów, zwanych granicami kontekstu. Krótko mówiąc, metoda z parametrem typu, A
która wymaga niejawnego parametru typu M[A]
:
def foo[A](implicit ma: M[A])
można przepisać jako:
def foo[A: M]
Ale jaki jest sens przekazywania niejawnego parametru bez nadawania mu nazwy? Jak może to być przydatne podczas wdrażania metody foo
?
Często nie trzeba odwoływać się bezpośrednio do niejawnego parametru, będzie on tunelowany jako niejawny argument do innej wywoływanej metody. Jeśli jest to potrzebne, nadal możesz zachować zwięzłą sygnaturę metody za pomocą Context Bound i wywołać, implicitly
aby zmaterializować wartość:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
Jawne przekazanie podzbioru niejawnych parametrów
Załóżmy, że wywołujesz metodę, która ładnie wypisuje osobę, używając podejścia opartego na klasie typów:
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)
A jeśli chcemy zmienić sposób wyświetlania nazwy? Możemy jawnie wywołać PersonShow
, jawnie przekazać alternatywę Show[String]
, ale chcemy, aby kompilator przekazał Show[Int]
.
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)