如何从实例传递隐式参数?

Lud*_*tey 1 scala scala-collections

我正在尝试定义一个类,该类将包含一个 Set 字段,并且希望能够直接从容器类中操作这个集合:

case class MyClass(prop: String) extends TraversableLike[Int,MyClass] {
   private def mySet: Set[Int]() = Set()

  override def foreach[U](f: Int => U) = data.foreach[U](f)

  override def newBuilder: Builder[Int, MyClass] =
    new ArrayBuffer[Int] mapResult (a => MyClass(prop, a.toSet))

  implicit def canBuildFrom: CanBuildFrom[MyClass, Int, MyClass] =
    new CanBuildFrom[MyClass, Int, MyClass] {
      def apply(): Builder[Int, MyClass] = newBuilder
      def apply(from: MyClass): Builder[Int, MyClass] = newBuilder
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望能够做到

var obj = MyClass("hello")
obj += 1
obj = obj map (_+1)
Run Code Online (Sandbox Code Playgroud)

第一条指令 (obj+= 1) 有效,但第二条无效。问题是我无法将隐式 canBuildFrom 放入对象 MyClass 中,因为构建器需要依赖于实例的信息(在本例中为 prop 字段)。

是否有解决方案使我的隐式可访问并保持其实例依赖性?我想避免使我的班级可变。

Jea*_*let 5

您的代码有几个问题:

  • Set[Int]()不是 的有效类型mySet,您应该删除()
  • 成员mySet应该是 a val,而不是 adef
  • 您正在调用不存在的 apply() 方法
  • 如果您想在 Traversable 级别挂钩到集合层次结构,则不会获得像+和 派生+=. 如果你代表一个集合,那么它应该是一个Set.

这是修改后的尝试:

import mutable.Builder
import generic.CanBuildFrom

class MyClass private (val prop: String, private val mySet: Set[Int] = Set())
    extends immutable.Set[Int] with SetLike[Int, MyClass] {

  def -(elem: Int) = MyClass(prop, mySet - elem)
  def +(elem: Int) = MyClass(prop, mySet + elem)
  def contains(elem: Int) = mySet.contains(elem)
  def iterator = mySet.iterator

  override def empty: MyClass = MyClass(prop)

  override def stringPrefix = "MyClass(" + prop + ")"
}

object MyClass {

  def DefaultProp = "DefaultProp"

  def apply(prop: String, mySet: Set[Int] = Set()) = new MyClass(prop, mySet)

  def newBuilder(prop: String = DefaultProp): Builder[Int, MyClass] =
    Set.newBuilder[Int] mapResult (set => MyClass(prop, set))

  implicit def canBuildFrom: CanBuildFrom[MyClass, Int, MyClass] =
    new CanBuildFrom[MyClass, Int, MyClass] {
      def apply(): Builder[Int, MyClass] = newBuilder()
      def apply(from: MyClass): Builder[Int, MyClass] = newBuilder(from.prop)
    }

}
Run Code Online (Sandbox Code Playgroud)

然后你可以写:

var obj = MyClass("hello")
obj += 1
println(obj) // prints MyClass(hello)(1)
obj = obj map (_ + 1)
println(obj) // prints MyClass(hello)(2)
Run Code Online (Sandbox Code Playgroud)

让我们剖析一下:

MyClass现在明确地是一个不可变集,在类型参数中声明了一个自定义表示SetLikeprop是公共 val 成员;实际集合mySet是一个私有值。

然后我们需要实现所Set依赖的四个操作,只需将mySet. (这看起来可以被分解。对于Seqs,有一个类SeqForwarder可以完成类似的工作;不过我找不到SetForwarder)。最后,我们提供了一个empty方法,内置的继承构建器也依赖于该方法。最后,覆盖stringPrefix可以使用 "MyClass" 和 的值实现更好的字符串表示prop

请注意,canBuildFrom object MyClass调用newBuilder,尽可能传递prop原始集合的 。这意味着大多数情况下,您可以在映射等MyClass实例时保留此值。prop然而,我们需要为 定义一个默认值,因为CanBuildFroms 必须定义一个apply不告诉原始集合是什么的方法。(问题:为什么会发生这种情况?)

最后,我们的实现newBuilder不再依赖于ArrayBuffers,而是直接构建Set将由您的新MyClass实例包装的实例。

更多资源: