Может кто-нибудь объяснить мне неявные преобразования в Scala?

А более конкретно, как работает BigInt для преобразования int в BigInt?

В исходном коде это гласит:

...
implicit def int2bigInt(i: Int): BigInt = apply(i)
...

Как вызывается этот код?

Я могу понять, как работает этот другой пример: "литералы даты".

В.

val christmas = 24 Dec 2010  

Определяется:

implicit def dateLiterals(date: Int) = new {
  import java.util.Date  
  def Dec(year: Int) = new Date(year, 11, date)
}

когда int получить передал сообщение Dec с int в качестве параметра система ищет другой метод, который может обработать запрос, в этом случае Dec(year:Int)

Q1. Правильно ли я понимаю мои литералы Date?

Q2. Как это относится к BigInt?

Спасибо

3 ответа

Когда предоставленный тип не соответствует ожидаемому типу, компилятор Scala ищет любой метод в области видимости, помеченный как неявный, который принимает предоставленный тип в качестве параметра и возвращает ожидаемый тип в результате. Если найдено, он вставляет вызов метода между ними. В случае BigInt, скажем, у вас есть метод

doSomethingWithBigInt(d:BigInt)=....

И вы называете это целым числом:

doSomethingWithBigInt(10)

Поскольку типы не совпадают, компилятор Scala сгенерирует:

doSomethingWithBigInt(int2bigInt(10))

Предполагая, что неявный int2bigInt находится в области видимости

Смысл неявных вещей заключается в том, чтобы заполнять скучные шаблоны, когда есть только один правильный способ сделать это.

В случае неявных параметров компилятор вставляет параметр из контекста, который должен быть тем, о чем вы думали. Например,

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 на Яве).)

Дополняя ответ @GClaramunt.

Потому что это намного проще понять и понять эту концепцию, увидев полный пример:

// define a class
case class Person(firstName: String, lastName: String)

// must import this to enable implicit conversions
import scala.language.implicitConversions

// define the implicit conversion. String to Person in this case
implicit def stringToPerson(name:String) = {
  val fields = name.split(" ");
  Person(fields(0), fields(1))
}

// method using the implicit conversion  
def getPerson(fullName:String): Person = fullName

val fooBar = getPerson("foo bar")
println(fooBar.getClass())  // class Person
println(fooBar.firstName)  // foo
println(fooBar.lastName)  // bar

Я надеюсь, что этот пример проясняет, почему и как можно использовать неявные преобразования (не то, что я думаю, что преобразование String в Person имеет много смысла, но это стоит иллюстрации).

Другие вопросы по тегам