2010-01-08 20 views
6

podría estar acercándose a mal, pero me gustaría tener un objeto como éste:Scala: ¿Cómo puedo implementar un método de clonación en una superclase y usarlo en una subclase?

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 

continuación:

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

Por lo tanto, el problema es que dataClone es de tipo MyDataStructure , no MyDataStructureExtended como yo esperaba.

Pensé en agregar un tipo T a la súper clase, que la subclase puede especificar (por ejemplo, sí), pero eso no parecía muy prometedor.

Respuesta

4

Suponiendo que se desea reducir al mínimo la cantidad de ceremonia en las subclases, aquí está mi sugerencia:

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] 
} 

Al extender java.lang.Cloneable y llamando al método Object.clone, se asegura de que su tiempo de ejecución es de tipo lo mismo que el objeto que se clona. El tipo estático se coacciona con un molde de tipo (asInstanceOf [T]). Tendrá que anular el método myClone en cada subclase y especificar el tipo, pero debe ser de una sola línea.

0

Es difícil decir si lo está haciendo bien con una descripción de problema tan vaga, pero en realidad es bastante sencillo hacerlo. Simplemente puede anular myclone en MyDataStructureExtended de manera que devuelva el tipo más específico. Cuando tenga una variable del tipo más específico, también podrá usar el método de clonación más específico.

código de ejemplo en el caso de que la descripción no era clara:

class A { 
    def getMe = this 
} 

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

y una sesión REPL correspondiente:

scala> val a = new A 
a: A = [email protected] 

scala> val b = new B 
b: B = [email protected] 

scala> a.getMe 
res0: A = [email protected] 

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 
5

Como se ha sugerido, tipos abstractos, o parámetros genéricos, son lo que necesita. ¿Requiere que MyDataStructure no sea un rasgo o clase abstracta? Lo siguiente define a MyDataStructure como una clase abstracta, pero también puede ser un rasgo.

abstract class MyDataStructure { 
    type T 
    def myClone: T 
} 

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

Los resultados de la serie de intérprete Scala de que el método myClone definido en MyDataStructureExtended es del tipo correcto.

scala> val mde = new MyDataStructureExtended("foo") 
val mde = new MyDataStructureExtended("foo") 
mde: MyDataStructureExtended = [email protected] 
scala> val cloned = mde.myClone 
val cloned = mde.myClone 
cloned: MyDataStructureExtended = [email protected] 

Es posible que desee restringir T de manera que su tipo sólo puede ser la de MyDataStructure subclases

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

No sé sus necesidades, pero yo creo que Scala 2.8 tendrá algunas funciones agradable con clases de casos y argumentos con nombre que permiten clonar clases de casos con un método de copia.

Cuestiones relacionadas