Может кто-нибудь объяснить мне неявные преобразования в Scala? - PullRequest
10 голосов
/ 19 мая 2010

А более конкретно, как 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 get передает сообщение Dec с int в качестве параметра, система ищет другой метод, который может обработать запрос, в данном случае Dec(year:Int)

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

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

Спасибо

Ответы [ 3 ]

15 голосов
/ 19 мая 2010

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

doSomethingWithBigInt(d:BigInt)=....

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

doSomethingWithBigInt(10)

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

doSomethingWithBigInt(int2bigInt(10))

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

11 голосов
/ 19 мая 2010

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

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

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).)

1 голос
/ 28 декабря 2018

Дополняет ответ @ 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 имеет большой смысл, но оно того стоит).

...