Смысл неявного материала заключается в том, чтобы заполнять скучный шаблонный материал, когда есть только один правильный способ сделать это.
В случае неявных параметров компилятор вставляет параметр из контекста, который должен быть тем, о чем вы думали. Например,
case class TaxRate(rate: BigDecimal) { }
implicit var sales_tax = TaxRate(0.075)
def withTax(price: BigDecimal)(implicit tax: TaxRate) = price*(tax.rate+1)
scala> withTax(15.00)
res0: scala.math.BigDecimal = 16.1250
Поскольку мы пометили налоговую ставку как неявный параметр и предоставили неявную переменную, которая может быть заполнена при необходимости, нам не нужно указывать налоговую ставку. Компилятор автоматически заполняет withTax(15.00)(sales_tax)
В случае неявных преобразований компилятор ищет метод, который может взять имеющийся у него тип и преобразовать его в нужный тип. Это преобразование не может быть приковано цепью при нормальных обстоятельствах, поэтому вы должны добраться до того, что вам нужно, за один шаг.
Есть два случая, когда неявные преобразования могут вступить в игру. Один из них находится в параметре вызова метода - если тип неправильный, но он может быть преобразован в правильный тип (точно одним способом), то компилятор преобразует для вас. Другой находится в присутствии вызова метода - если фактически используемый тип не имеет доступного метода, но вы можете преобразовать его в тип, который имеет этот метод, тогда преобразование займет место, а затем будет вызван метод.
Давайте рассмотрим пример каждого.
implicit def float2taxrate(f: Float) = TaxRate(BigDecimal(f))
scala> withTax(15.00)(0.15f)
res1: scala.math.BigDecimal = 17.250000089406967200
Здесь мы называем явную ставку налога 0.15f
. Это не соответствует параметру, который должен иметь тип TaxRate
, но компилятор видит, что мы можем превратить плавающие ставки в налоговые ставки, используя неявный float2taxrate
. Так что он делает это для нас, звоня withTax(15.00)(float2taxrate(0.15f))
Теперь другой пример.
class Currency(bd: BigDecimal) {
def rounded = bd.setScale(2,BigDecimal.RoundingMode.HALF_EVEN)
}
implicit def bigdec2currency(bd: BigDecimal) = new Currency(bd)
scala> withTax(15.00)(0.15f).rounded
res66: scala.math.BigDecimal = 17.25
BigDecimal не имеет rounded
метода, поэтому withTax(15.00)(0.15f)
не должен вызывать его (так как он возвращает BigDecimal
). Но мы определили Currency
, который имеет метод rounded
и преобразование в Currency
, поэтому неявное преобразование заполняет все детали: bigdec2currency(withTax(15.00)(0.15f)).rounded
.
В случае преобразования из Int
в BigInt
компилятор будет использовать его, например, когда попытается добавить 7 + BigInt(5)
. Это не будет работать нормально - 7
- это Int
, а Int
не знает, как добавить себя к BigInt
. Но у BigInt
есть метод +
, который может добавить себя к другому BigInt
. И компилятор видит, что если бы он мог преобразовать 7
в BigInt
, он мог бы использовать этот метод. Неявное преобразование позволяет это преобразование, поэтому оно переводит 7 + BigInt(5)
в int2bigInt(7)+BigInt(5)
.
(Примечание: int2bigInt
определено внутри BigInt
, поэтому для его использования необходимо import BigInt._
. А это, в свою очередь, относится к методу apply(i: Int)
объекта BigInt
, что позволяет вам написать BigInt(5)
и заставить его работать (вместо того, чтобы передавать строку, как с BigInteger
в Java).)