Я пытаюсь добавить новые функции к существующим типам (чтобы IDE автоматически предлагала соответствующие функции для типов, которые я не могу контролировать, например Future[Option[A]]). Я исследовал как неявные классы, так и неявные преобразования, чтобы добиться этого, и оба они, похоже, предлагают одинаковое поведение.
Есть ли какая-либо эффективная разница между использованием неявного класса:
case class Foo(a: Int)
implicit class EnrichedFoo(foo: Foo) {
def beep = "boop"
}
Foo(1).beep // "boop"
И используя неявное преобразование:
case class Foo(a: Int)
trait Enriched {
def beep: String
}
implicit def fooToEnriched(foo: Foo) = new Enriched {
def beep = "boop"
}
Foo(1).beep // "boop"
Я предполагаю, что одно отличие здесь может заключаться в том, что в первом примере вместо трейта создается одноразовый класс, но я мог бы легко адаптировать неявный класс для расширения абстрактного трейта, например:
case class Foo(a: Int)
trait Enriched {
def beep: String
}
implicit class EnrichedFoo(foo: Foo) extends Enriched {
def beep = "boop"
}
Foo(1).beep // "boop"
Ответ 1 · рейтинг 7 · Luka Jacobowitz
Насколько я знаю, они почти одинаковые. Правила области видимости также в равной степени применяются к обоим.
На мой взгляд, я бы использовал implicit classes для вашей ситуации. Вероятно, они были созданы именно для чего-то подобного.
Для меня неявные преобразования более уместны, когда у вас уже есть два разных класса и вы хотите преобразовать их между ними.
Вы можете ознакомиться с первоначальным предложением по неявным классам здесь. Там сказано:
Предлагается новая языковая конструкция для упрощения создания классов, предоставляющих методы расширения для другого типа.
Вы даже можете увидеть, как он обесахаривает implicit classes. Следующее:
implicit class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = if (n <= m) n else m
...
}
будет обессахариваться в:
class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = if (n <= m) n else m
...
}
implicit final def RichInt(n: Int): RichInt = new RichInt(n)
Ответ 3 · AyJayKay
Чтобы добавить ответ Луки Якобовица: неявные классы в основном являются расширениями. Неявное преобразование используется, чтобы сообщить компилятору, что его можно рассматривать как что-то с расширением.
Звучит почти так же. Две интересные вещи из неявного преобразования имеют некоторую разницу:
Во-первых: вам может потребоваться активировать языковую функцию для отключения предупреждений при использовании неявного преобразования.
Во-вторых: термин "преобразование" типа может сбивать с толку:
Неявные преобразования применяются в двух ситуациях: Если выражение e имеет тип S, а S не соответствует ожидаемому типу выражения T. [Или:] При выборе em с e типа S, если селектор m не обозначает член С.
case class Foo(a: Int)
trait Enriched {
def beep: String
}
implicit def fooToEnriched(foo: Foo) = new Enriched {
def beep = "boop"
}
Foo(1) match {
case _:Enriched => println("is an Enriched")
case _:Foo => println("no, was a Foo")
}
// no, was a Foo
но его можно рассматривать как обогащенный...
val enriched: Enriched = Foo(2)
enriched match {
case _:Enriched => println("is an Enriched")
case _:Foo => println("no, was a Foo")
}
// is an Enriched
// plus unreachable code warning: case _:Foo => println("no, was a Foo")