我们假设,我们有一个带有类ImmutableFoo
的不可变库:
scala> class ImmutableFoo(x: Int) {
def add(y: Int): ImmutableFoo = new ImmutableFoo(x + y)
}
defined class ImmutableFoo
当然,此类的对象不会通过向y
添加x
来更改其状态,但会创建该类的新对象。到目前为止一切都很好。
现在我们要创建一个不可变的子类。首先尝试:
scala> class subImmutableFoo(x: Int) extends ImmutableFoo(x)
defined class subImmutableFoo
但是当我们添加一个数字时,我们不会得到子类的对象:
scala> (new subImmutableFoo(5)).add(6)
res0: ImmutableFoo = ImmutableFoo@1ee69d3
解决此问题的最佳做法是什么?
一种解决方案可能是覆盖子类中的方法并创建子类的对象。在这种情况下,这个解决方案很简单,但是当方法更大时,可能会导致有很多双重代码(基类+子类)。当我们创建mutliple子类并覆盖该方法时,甚至更多的双重代码。
答案 0 :(得分:1)
将其视为MyType
问题,您可以这样做:
class ImmutableFoo[T <: ImmutableFoo[T] : Manifest](x: Int) {
def add(y: Int): T = implicitly[Manifest[T]].erasure.getDeclaredConstructor(classOf[Int]).newInstance((x+y).asInstanceOf[Object]).asInstanceOf[T]
}
class SubImmutableFoo(x: Int) extends ImmutableFoo[SubImmutableFoo](x)
new SubImmutableFoo(2).add(4)
您可以为Addable
操作定义add
类型类。这种情况的代码太多了,但这种模式更通用。
trait Addable[T] {
def plus(a:T, i:Int):T
class Infix(x:T) {
def add(i:Int):T = plus(x, i)
}
}
class ImmutableFoo(val x:Int)
class SubImmutableFoo(override val x:Int) extends ImmutableFoo(x)
implicit object AddableFoo extends Addable[ImmutableFoo]{
override def plus(a:ImmutableFoo, i:Int) = new ImmutableFoo(a.x+i)
}
implicit object AddableSub extends Addable[SubImmutableFoo]{
override def plus(a:SubImmutableFoo, i:Int) = new SubImmutableFoo(a.x+i)
}
implicit def infix[T: Addable](x:T) = {
val addable = implicitly[Addable[T]]
new addable.Infix(x)
}
new ImmutableFoo(3) add 4 // ImmutableFoo
new SubImmutableFoo(2) add 1 // SubImmutableFoo
答案 1 :(得分:1)
在“MyType”模式的变体上,您可以执行以下不使用清单或反射的内容:
trait Immu[+Repr <: Immu[Repr]] {
def x: Int
def add(y: Int): Repr = newInstance(x + y)
protected def newInstance(x: Int): Repr
}
class Foo(val x: Int) extends Immu[Foo] {
def show: String = "Immu(" + x + ")"
protected def newInstance(x: Int) = new Foo(x)
}
class Bar(x0: Int) extends Foo(x0) with Immu[Bar] {
def mul(y: Int): Bar = newInstance(x * y)
override protected def newInstance(x: Int) = new Bar(x)
}
new Foo(3).add(4).show
new Bar(3).add(4).mul(2).show
当然,反过来,它假定您不会改变所需的构造函数参数的数量。