tags:

views:

240

answers:

3

I might be approaching this the wrong way, but I'd like to have an object like this:

class MyDataStructure {
  def myClone = {
    val clone = new MyDataStructure
    // do stuff to make clone the same as this
    ...
    clone
  }
}

class MyDataStructureExtended(val foo: String) extends MyDataStructure

Then:

val data = MyDataStructureExtended
val dataClone = data.clone
println(dataClone.foo)

So, the problem is that dataClone is of type MyDataStructure, not MyDataStructureExtended as I'd hoped.

I thought about adding a type T to the super class, that the subclass can specify (e.g. itself), but that didn't seem very promising.

A: 

Hard to say whether you're doing it right with such a vague problem description, but it's actually pretty straightforward to do this. You can simply override myclone in MyDataStructureExtended such that it returns the more specific type. When you have a variable of the more specific type, you'll be able to use the more specific clone method as well.

Example code in case that description was unclear:

class A {
  def getMe = this
}

class B extends A {
  override def getMe = this
  def isAnInstanceOfB = true
}

And a corresponding REPL session:

scala> val a = new A
a: A = A@1a6eeab

scala> val b = new B
b: B = B@a36771

scala> a.getMe
res0: A = A@1a6eeab

scala> a.getMe.isAnInstanceOfB
<console>:7: error: value isAnInstanceOfB is not a member of A
       a.getMe.isAnInstanceOfB
           ^

scala> b.isAnInstanceOfB      
res2: Boolean = true

scala> b.getMe.isAnInstanceOfB
res3: Boolean = true
David Winslow
+4  A: 

As you have suggested, abstract types, or generic parameters, are what you need. Do you require that MyDataStructure not be a trait or abstract class? The following defines MyDataStructure to be an abstract class, but you can make it a trait as well.

abstract class MyDataStructure {
  type T
  def myClone: T
}

class MyDataStructureExtended(foo: String) extends MyDataStructure {
  type T = MyDataStructureExtended
  def myClone = new MyDataStructureExtended(foo)
}

The results from the Scala interpreter show that the myClone method defined in MyDataStructureExtended is the correct type.

scala> val mde = new MyDataStructureExtended("foo")
val mde = new MyDataStructureExtended("foo")
mde: MyDataStructureExtended = MyDataStructureExtended@3ff5d699
scala> val cloned = mde.myClone
val cloned = mde.myClone
cloned: MyDataStructureExtended = MyDataStructureExtended@2e1ed620

You might want to restrict T so that its type can only be that of MyDataStructure subclasses

abstract class MyDataStructure {
  type T <: MyDataStructure
  def myClone: T
}

I don't know your requirements, but I believe that Scala 2.8 will have some nice functionality with case classes and named arguments that allow one to clone case classes with a copy method.

faran
+4  A: 

Assuming you want to minimize amount of ceremony in the subclasses, here is my suggestion:

class A extends Cloneable {
  protected[this] def myCloneImpl[T] = {
    val justLikeMe = this.clone
    // copy values and such.
    // Note that the Object.clone method already made a shallow copy, but you may want
    // to deepen the copy or do other operations.
    justLikeMe.asInstanceOf[T]
  }
  def myClone = myCloneImpl[A]
}

class B extends A {
  override def myClone = myCloneImpl[B]
}

By extending java.lang.Cloneable and calling the Object.clone method, you ensure that your runtime type is the same as the object being cloned. The static type is coerced with a type-cast (asInstanceOf[T]). You will need to override the myClone method in each subclass and specify the type, but it should be a one-liner.

Mitch Blevins