泛型子类型的隐式 class class

Implicit class for subtypes of a generic class

我想用一些自定义代码增强所有 Iterable。 为此,我写了以下内容:

implicit class RichIterable[A, B <: Iterable[A]](b: B) {
  def nonEmptyOpt: Option[B] = if (b.nonEmpty) Some(b) else None
}

现在,当我想在 List 上使用此方法时,它绝对是 Iterable 的子类,就像这样

List(1, 2, 3).nonEmptyOpt

我明白了

value nonEmptyOpt is not a member of List[Int]

我该如何解决这个问题?

曾经偶然发现的小技巧:

scala> implicit class RichIterable[A, B <: Iterable[A]](b: B with Iterable[A]) {
 |   def nonEmptyOpt: Option[B] = if (b.nonEmpty) Some(b) else None
 | }
defined class RichIterable

scala> List(1,2,3).nonEmptyOpt
res3: Option[List[Int]] = Some(List(1, 2, 3))

注意参数上的B with Iterable[A]

顺便说一句,在调试隐式时,有时尝试显式应用它们(更改之前)会有所帮助:

scala> new RichIterable(List(1,2,3)).nonEmptyOpt
<console>:15: error: inferred type arguments [Nothing,List[Int]] do not conform to class RichIterable's type parameter bounds [A,B <: Iterable[A]]
          new RichIterable(List(1,2,3)).nonEmptyOpt

因此,编译器很难确定 A 的类型。类型细化显然有助于它。

给定一个只有 B <: Iterable[A] 类型的参数,编译器不知道如何轻松地找出 A 是什么,因为它不一定很容易从 B 计算出来(需要搜索最小上限)。

相反,您可以通过重新定义类型约束来做到这一点,而无需使用技巧。本质上,B 实际上应该是一个 类型的构造函数 ,它在 Iterable 之上。然后,您的隐式 class 是从一些 B[A] 到您丰富的 class 的转换。具有 B[A] 的参数有助于编译器计算 A,因为它期望它是类型构造函数 B.

的参数
implicit class RichIterable[A, B[X] <: Iterable[X]](b: B[A]) {
  def nonEmptyOpt: Option[B[A]] = if (b.nonEmpty) Some(b) else None
}

scala> List(1, 2, 3).nonEmptyOpt
res0: Option[List[Int]] = Some(List(1, 2, 3))

scala> List.empty[Int].nonEmptyOpt
res1: Option[List[Int]] = None

一个更简单的解决方案是:

implicit class RichIterable[A](b: Iterable[A]) {
  def nonEmptyOpt: Option[Iterable[A]] = if (b.nonEmpty) Some(b) else None
}

scala> List(1,2,3).nonEmptyOpt
res0: Option[Iterable[Int]] = Some(List(1, 2, 3))