我正在尝试向现有类型添加新功能(因此我可以让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"
答案 0 :(得分:7)
据我所知,它们几乎完全一样。范围规则同样适用于两者。
在我看来,我会根据您的情况使用implicit classes
。它们可能就是为了类似的东西创建的。
对于我来说,当你已经实际拥有两种不同类并希望在两者之间进行转换时,隐式转换会更合适。
您可以查看隐式类right here.的初始提案 它说:
提出了一种新的语言结构来简化类的创建,这些类为另一种类型提供扩展方法。
你甚至可以看到它如何消亡implicit classes
。以下内容:
implicit class RichInt(n: Int) extends Ordered[Int] {
def min(m: Int): Int = if (n <= m) n else m
...
}
将desugar into:
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)
答案 1 :(得分:1)
对我来说这是一个偏好的问题。实际上,implicit classes
的出现是为了简化为另一种类型提供扩展方法的类的创建。
隐式类虽然为value classes
增加了很多价值。
答案 2 :(得分:0)
要补充Luka Jacobowitz的答案:隐式类基本上是扩展。 Implicit conversion用于告诉编译器它可能被视为具有扩展名的东西。
声音几乎相同。隐式转换带来的两个有趣的方面有所不同:
首先:在使用隐式转换时,您可能需要激活语言功能以禁用警告。
第二:“转换”类型的术语可能会造成混淆:
隐式转换在两种情况下适用: 如果表达式e的类型为S,而S不符合表达式的预期类型T。 [或:]如果选择器m不表示S的成员,则选择e的S类型为e。
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")