Вот несколько причин использовать восхитительно простой метод implicitly
.
Чтобы понять / устранить неполадки неявных представлений
Неявное представление может быть запущено, когда префикс выбора (например, the.prefix.selection(args)
не содержит члена selection
, который применим к args
(даже после попытки преобразовать args
с неявными представлениями) В этом случае компилятор ищет неявные члены, локально определенные в текущей или включающих областях, наследуемые или импортированные, которые являются либо функциями из типа этого the.prefix
к типу с определенным selection
, либо эквивалентным неявные методы.
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
Неявные представления также могут запускаться, когда выражение не соответствует ожидаемому типу, как показано ниже:
scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1
Здесь компилятор ищет эту функцию:
scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>
Доступ к неявному параметру, вводимому ограниченным контекстом
Неявные параметры, возможно, являются более важной функцией Scala, чем неявные представления. Они поддерживают шаблон класса типа. Стандартная библиотека использует это в нескольких местах - см. scala.Ordering
и как это используется в SeqLike#sorted
. Неявные параметры также используются для передачи манифестов массива и CanBuildFrom
экземпляров.
Scala 2.8 допускает сокращенный синтаксис для неявных параметров, называемый контекстными границами. Вкратце, метод с параметром типа A
, для которого требуется неявный параметр типа M[A]
:
def foo[A](implicit ma: M[A])
можно переписать как:
def foo[A: M]
Но какой смысл передавать неявный параметр, а не называть его? Как это может быть полезно при реализации метода foo
?
Часто на неявный параметр не нужно ссылаться напрямую, он будет туннелироваться как неявный аргумент для другого вызываемого метода. Если это необходимо, вы все равно можете сохранить краткую подпись метода с привязкой к контексту и вызвать implicitly
, чтобы материализовать значение:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
Явная передача подмножества неявных параметров
Предположим, вы вызываете метод, который довольно печатает человека, используя подход на основе классов типов:
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)
Что если мы хотим изменить способ вывода имени? Мы можем явно вызвать PersonShow
, явно передать альтернативу Show[String]
, но мы хотим, чтобы компилятор передал Show[Int]
.
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)