Неявное преобразование, требуется импорт или нет?

Я пишу

object MyString {
  implicit def stringToMyString(s: String) = new MyString(s)    
}

class MyString(str: String) {
  def camelize = str.split("_").map(_.capitalize).mkString

  override def toString = str
}


object Parse {
  def main(args: Array[String]) {
    val x = "active_record".camelize
    // ...
  }
}

в моей программе. Это вызывает компиляционную ошибку. После того, как я вставил

  import MyString.stringToMyString

Затем он работает.

От Odersky Programming в Scala Я понял, что неявное преобразование в сопутствующем объекте источника или ожидаемых целевых типов не нужно импортировать.

Ответ 1

неявное преобразование в компаньоне объект источника или ожидаемый целевые типы не обязательно должны быть импортирован.

Верно. Теперь метод camelize определен в классе MyString, и, действительно, в его объектном компаньоне есть неявное преобразование в MyString. Однако в коде не сказано ничего, говорящее компилятору, что MyString - ожидаемый тип цели.

Если вместо этого вы написали это:

val x = ("active_record": MyString).camelize

тогда это сработало бы, потому что компилятор знал бы, что вы ожидаете, что "active_record" будет MyString, заставив его искать неявное преобразование внутри объекта MyString.

Это может выглядеть немного рестриктивно, но на самом деле оно работает в нескольких местах. Скажем, например, у вас было:

class Fraction(num: Int, denom: Int) {
    ...
    def +(b: Fraction) = ...
    ...
}

И у вас появился такой код:

val x: Fraction = ...
val y = x + 5

Теперь x имеет метод +, ожидаемый тип которого Fraction. Таким образом, компилятор будет выглядеть здесь для неявного преобразования из Int в Fraction внутри объекта Fraction (и внутри объекта Int, если он был, так как это тип источника).

Ответ 2

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

object Parse {
  def foo(s: MyString) = s.camelize

  def main(args: Array[String]) {
    val x = foo("active_record")
    println(x.toString)
  }
}

Смотрите Pimp my library pattern, на основе статья Мартина:

Обратите внимание, что невозможно установить defs на верхнем уровне, поэтому вы не можете определить неявное преобразование с глобальной областью. Решение состоит в том, чтобы поместить def внутри объекта, а затем импортировать его, т.е.

object Implicits {
    implicit def listExtensions[A](xs : List[A]) = new ListExtensions(xs)
}

И затем вверху каждого исходного файла вместе с вашим другим импортом:

import Implicits._

Ответ 3

Я попробовал пример класса Rational в программировании в книге Scala, поместил неявный метод в свой сопутствующий объект:

object Rational {
  implicit def intToRational(num: Int) = 
    new Rational(num)
}

но код

2 + new Rational(1, 2)

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