隐式 class 与隐式转换为特征

Implicit class vs Implicit conversion to trait

我正在尝试向现有类型添加新函数(因此我可以让 IDE 自动为我无法控制的类型建议相关函数,例如 Future[Option[A]])。我探索了隐式 classes 和隐式转换来实现这一点,它们似乎都提供相同的行为。

使用隐式 class:

之间有什么有效的区别吗
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"

我想这里的一个区别可能是第一个示例创建了一次性 class 而不是特征,但我可以轻松地调整隐式 class 来扩展抽象特征,例如:

case class Foo(a: Int)
trait Enriched {
  def beep: String
}
implicit class EnrichedFoo(foo: Foo) extends Enriched {
  def beep = "boop"
}
Foo(1).beep // "boop"

据我所知,它们几乎完全一样。范围规则同样适用于两者。

在我看来,我会根据您的情况使用 implicit classes。它们可能正是为类似的东西而创建的。

隐式转换,对我来说,当你实际上已经有两种不同的 类 并且想在两者之间进行转换时更合适。

您可以查看隐含的初始提案 类 right here. 上面写着:

A new language construct is proposed to simplify the creation of classes which provide extension methods to another type.

您甚至可以看到它是如何脱糖的 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)

对我来说这是一个偏好问题。实际上 implicit classes 的出现是为了简化 类 的创建,它为另一种类型提供扩展方法。 不过,隐式 类 为 value classes 添加了很多价值。

添加 Luka Jacobowitz 的回答:隐式 类 基本上是扩展。 Implicit conversion 用于告诉编译器,它可能被视为带有扩展名的东西。

听起来几乎一样。隐式转换的两个有趣之处有一些区别:

首先:您可能需要激活语言功能以在使用隐式转换时禁用警告。

其二:"converting"类型的术语可能会混淆:

Implicit conversions are applied in two situations: If an expression e is of type S, and S does not conform to the expression’s expected type T. [Or:] In a selection e.m with e of type S, if the selector m does not denote a member of S.

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

但它可能被视为 Enriched...

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