泛型类的子类型的隐式类

时间:2016-08-25 15:16:28

标签: scala implicit

我希望使用一些自定义代码增强所有before(:create) do |article| article.reviewers << FactoryGirl.build(:reviewer, article: article) end 。 为此,我写了以下内容:

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

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:7)

我偶然发现了一个小技巧:

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]

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

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的类型。类型细化显然有助于它。

答案 1 :(得分:7)

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

相反,您可以通过重新定义类型约束来实现此目的,而无需使用技巧。从本质上讲,B应该是一个类型的构造函数,它由Iterable限制在上面。然后,您的隐式类是从某些B[A]到您的丰富类的转换。参数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

答案 2 :(得分:0)

更简单的解决方案是:

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