隐式 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")
我正在尝试向现有类型添加新函数(因此我可以让 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")