在我的类型及其子类型的 Iterable 上隐式 class

Implicit class on Iterable of my type and its subtypes

我正在尝试学习使用 Scala 的丰富可能性创建 DSL 的良好实践,但我遇到了以下问题:我无法创建适用于所有子类型的隐式 class可迭代(数组,列表,......)并且仅匹配我定义的特定类型的子类型(在示例中:Fruit)。 这是我到目前为止的想法(这是我的代码的简化版本):

sealed trait Fruit { // My main type
  type A <: Fruit
  def setSize(v: Int): Unit = this match {
    case multi: MultiFruit[_] => multi.foreach(_.setSize(v))
    case or: Orange => or.size = v
    case ap: Apple => ap.size = v
    case _ => throw new Exception
  }
}
case class MultiFruit[F <: Fruit](var l: List[F]) extends Fruit {
  type A = F
  def foreach[B](f: F => B) : Unit = l.foreach(f)
}
case class Orange(var size: Int) extends Fruit {
  type A = Orange
}
case class Apple(var size: Int) extends Fruit {
  type A = Apple
}
object Fruit {
  implicit class IterableFruit[F <: Fruit, I <: Iterable[F]](val ite: I) extends AnyVal {
    def setSize(v: Int): Unit = ite.foreach(_.setSize(v)) // Apply setSize on every Fruit of the iterable
    def ++[F2 <: Fruit, I2 <: Iterable[F2]](ite2: I2): MultiFruit[_ <: Fruit] = new MultiFruit[Fruit](ite.toList++ite2.toList)
  }
}

所以这是我的问题:当我尝试 运行 这个

object Main {
  import Fruit._
  def main(args: Array[String]): Unit = {
    val oranges = Array.tabulate(5)(i => Orange(i*10))
    val apples = Array.tabulate(5)(i => Apple(i*10))
    oranges setSize 20 // setSize is not found, it doesn't match Iterable[Fruit]
    val or_ap = oranges ++ apples // ++ not found (the compiler wants to use the one defined in Array)
  }
}

找不到我的方法setSize 和++。 让我更加困惑的是我的 IDE (IntelliJ) 找到了 setSize 方法(并且没有报告任何错误)但是 IDE 认为 ++ 是数组中定义的而不是在我的隐式 class 中。 我认为我的问题来自于我输入 IterableFruit class 的方式,但是我在扩展方法上发现的资源非常有限(而且从未真正深入),我无法弄清楚哪里出了问题。

编辑: 另外,当我将隐式 class 修改为:

object Fruit {
  implicit class IterableFruit[F <: Fruit](val ite: Array[F]) extends AnyVal {
    def setSize(v: Int): Unit = ite.foreach(_.setSize(v)) // Apply setSize on every Fruit of the iterable
    def ++[F2 <: Fruit](ite2: Array[F2]): MultiFruit[_ <: Fruit] = new MultiFruit[Fruit](ite.toList++ite2.toList)
  }
}

一切都可以编译,但现在如果我尝试使用数组以外的任何其他可迭代对象显然不起作用,所以我认为问题确实来自我在隐式 class[ 中使用键入的方式=14=]

这可以通过使用 toIterable 将数组转换为 Iterables 来解决(Array 不扩展 Iterable,因为它代表一个内置的 Java 数组):

val oranges = Array.tabulate(5)(i => Orange(i*10)).toIterable

要使 ++ 方法起作用,您必须将其重命名为其他名称,例如 +:+。目前,编译器认为没有理由进行隐式转换,因为您的 oranges 对象上已经有一个 concat/++ 方法。

Link 到 Scastie