[Из песочницы] Неявные (implicit) параметры и преобразования в Scala
Пробежавшись по предыдущим статьям на Хабре, тыц и тыц так и не удалось в быстром режиме понять, что делает неявность (implicit) в Scala. Попробуем разобраться вместе.
Итак, implicit в Scala позволяют избежать вызывания методов или явных ссылок на переменные, и взамен этого позволяют компилятору самому найти нужные неявные данные.
Например, мы могли бы написать функцию для преобразования из Float в Int (FloatToInt) и, вместо того, чтобы вызвать эту функцию явно, компилятор бы сделал это вместо нас неявно:
def double(value: Int) = value * 2
implicit def FloatToInt(value: Float):Int = value.toInt
println(double(2.5F))
Запутанно? Давайте обо всём по порядку.
Итак, сегодня мы рассмотрим две категории implicit, а именно:
- Неявные параметры (implicit parameters, values). Они являются автоматически переданными компилятором значениями, объявленными через implicit.
- Неявное преобразование (implicit conversion). При несовпадении типа ожидаемого параметра с входящим параметром компилятор ищет любой метод в области видимости, отмеченный implicit и с нужными в данной ситуации ожидаемым параметром и входящим параметром и автоматически его передаёт.
Неявный параметр
Неявный параметр — это параметр функции, которому предшествует ключевое слово implicit. Оно значит, что, если не передано никакого значения при вызове функции, то компилятор собственноручно будет искать неявный параметр и передаст его за нас.
Например, такая функция, принимающая на вход неявный параметр value и удваивающая его:
def double(implicit value: Int) = value * 2
Без неявного параметра упадёт с ошибкой:
def double(implicit value: Int) = value * 2
println(double) // error: could not find implicit value for parameter value
С переданным явно параметром сработает:
def double(implicit value: Int) = value * 2
println(double(3)) // 6
С неявным параметром это будет выглядеть так:
def double(implicit value: Int) = value * 2
implicit val multiplier = 2
println(double) // 4
Но нужно быть аккуратным:
def double(implicit value: Int) = value * 2
implicit val multiplier = 2
implicit val multiplier2 = 1
println(double) // error: ambiguous implicit values
И напоследок пример с передачей в качестве неявного параметра def:
def double(implicit value: Int) = value * 2
val cappucinoLarge: Boolean = false
implicit def cappucinoPrice: Int = if (cappucinoLarge) 200 else 100
println(double) // 200
Т.е. в итоге у нас получится
double(cappucinoPrice())
Примечания по синтаксису:
def func1(implicit value1: Int) // value1 неявно
def func2(implicit value1: Int, value2: Int) // value1 и value2 неявны
def func3(value1: Int, implicit value2: Int) // ошибка компиляции
def func4(value1: Int)(implicit value2: Int) // только value2 неявно
def func5(implicit value1: Int)(value2: Int) // ошибка компиляции
def func6(implicit value1: Int)(implicit value2: Int) // ошибка компиляции
Неявное преобразование
Возвращаясь к примеру из Float в Int:
def double(value: Int) = value * 2
implicit def FloatToInt(value: Float):Int = value.toInt
println(double(2.5F))
При вызове double у нас происходит несовпадение типа ожидаемого параметра (Int) с входящим параметром (Float). Поэтому компилятор ищет любой метод в области видимости, отмеченный implicit и с нужными в данной ситуации ожидаемым параметром (Int) и входящим параметром (Float). Находит FloatToInt, производит преобразование и передает дальше нужное значение в double.
Теперь, надеюсь, стало понятнее. Всем успехов в освоении Scala!