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

时间:2011-04-30 06:38:59

标签: 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
    }
}

我希望能够做到

var obj = MyClass("hello")
obj += 1
obj = obj map (_+1)

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

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

1 个答案:

答案 0 :(得分:4)

您的代码存在一些问题:

  • Set[Int]()不是mySet的有效类型,您应放弃()
  • 成员mySet应该是val,而不是def
  • 您正在调用不存在的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)
    }

}

然后你可以写:

var obj = MyClass("hello")
obj += 1
println(obj) // prints MyClass(hello)(1)
obj = obj map (_ + 1)
println(obj) // prints MyClass(hello)(2)

让我们剖析一下:

MyClass现在显式为一个不可变集,其自定义表示形式在SetLike的类型参数中声明。 prop是公共成员;实际集合mySet是私有val。

然后我们需要实现Set依赖的四个操作,只需转发mySet即可。Seq。 (看起来好像可以将其考虑在内。对于SeqForwarder,有一个类SetForwarder执行类似的工作;但我找不到empty。最后,我们提供了一个stringPrefix方法,内置的继承构建器也依赖于该方法。最后,覆盖prop可以使用“MyClass”和canBuildFrom的值来启用更好的字符串表示。

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

最后,我们ArrayBuffer的实现不再依赖Set,而是直接构建将由新MyClass实例包装的{{1}}实例。

更多资源: