Ось кілька причин використовувати чудово простий метод implicitly
.
Щоб зрозуміти / усунути неполадки перегляду
Неявний перегляд може спрацьовувати, коли префікс виділення (розглянемо, наприклад, the.prefix.selection(args)
не містить члена, до selection
якого можна застосувати args
(навіть після спроби конвертувати за args
допомогою Implicit Views). У цьому випадку компілятор шукає неявних членів, локально визначених у поточних або додаючих областях, успадкованих або імпортованих, що є або функціями від типу цього типу 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, ніж Implicit Views. Вони підтримують модель класу типу. Стандартна бібліотека використовує це в кількох місцях - дивіться, 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)