Ответ 1
Вот несколько причин использовать восхитительно простой метод 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 позволяет использовать сокращенный синтаксис для неявных параметров, называемый Context Bounds. Вкратце, метод с параметром типа A
, который требует неявного параметра типа M[A]
:
def foo[A](implicit ma: M[A])
можно переписать как:
def foo[A: M]
Но какая точка передачи неявного параметра, но не называя его? Как это может быть полезно при реализации метода foo
?
Часто неявный параметр не нужно ссылаться напрямую, он будет туннелироваться как неявный аргумент для другого вызываемого метода. Если это необходимо, вы все равно можете сохранить красную подпись метода с помощью Context Bound и вызвать 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)