【问题标题】:Implicit class for subtypes of a generic class泛型类的子类型的隐式类
【发布时间】:2016-08-25 15:16:27
【问题描述】:

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

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


    【解决方案1】:

    给定一个只有B &lt;: 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
    

    【讨论】:

    • 只是好奇,“存在”会丢失/改变什么吗? RichIterable[A, B[_] &lt;: Iterable[_]](b: B[A]) {...
    • @jwvh 这将丢失所有不完全具有 1 个类型参数的子类的确切返回类型,这与它们扩展的 Iterable 中的相同。其中一些例子是Map[A, B]scala.xml.NodeSeq。例如,Map(1-&gt;2).nonEmptyOpt 将是 Option[Iterable[...]],而不是 Option[Map[...]]
    • @Kolmar,我一直在B[X] ...B[_] ... 之间来回走动,到目前为止,我无法辨别Map() 处理方面的任何区别。
    【解决方案2】:

    我曾经偶然发现的小技巧:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 这种方法的问题是您已经将 List 转换为 Iterable。
      猜你喜欢
      • 2018-11-21
      • 2015-01-19
      • 1970-01-01
      • 2013-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多