我希望使用一些自定义代码增强所有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
我该如何解决这个问题?
答案 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))