我正在尝试定义一个类,它将字段作为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字段)。
是否有解决方案使我的隐式可访问并保持其实例依赖? 我想避免让我的班级变得可变。
答案 0 :(得分:4)
您的代码存在一些问题:
Set[Int]()
不是mySet
的有效类型,您应放弃()
mySet
应该是val
,而不是def
+
和派生+=
等方法。如果您要表示一个集合,那么它应该是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}}实例。
更多资源: