2012-04-24 8 views
5

Tengo dos clases de casos que heredan de una clase base abstracta. Quiero definir algunos métodos en la clase base abstracta que usan los métodos de copia en las clases de casos heredadas (y así devolver una instancia de la clase hija). ¿Hay alguna manera de hacer esto usando self types?Scala: ¿Hay alguna manera para que una clase principal acceda a los métodos definidos solo por los niños?

código Ejemplo:

abstract class BaseClass(a: String, b: Int) { 
    this: case class => //not legal, but I'm looking for something similar 

    def doubleB(newB: Int) = this.copy(b = b * 2) //doesn't work because BaseClass has no copy 
} 

case class HasC(a: String, b: Int, c: Boolean) extends BaseClass(a, b) { 
    def doesStuffWithC(newC: Boolean) = { 
    ... 
    } 
} 

case class HasD(a: String, b: Int, D: Double) extends BaseClass(a, b) { 
    def doesStuffWithD(newD: Double) = { 
    ... 
    } 
} 

he descubierto la manera de obtener el resultado que quiero gracias a esta pregunta: How to use Scala's this typing, abstract types, etc. to implement a Self type? pero implica la adición de un método makeCopy a BaseClass y la sustituya por una llamada a copie en cada una de las clases de casos del niño, y la sintaxis (especialmente para el tipo de uno mismo) es bastante confusa. ¿Hay alguna manera de hacer esto con la autodescripción incorporada de Scala?

+2

Alguien respondió que todas las clases de caso extienden 'Producto', lo cual es cierto, pero se eliminó, presumiblemente porque' copy' no está definido en 'Product', por lo que no funciona para usarlo para el tipo propio. El documento de "Producto" dice "todas las clases de casos implementan el Producto con métodos sintéticamente generados", ¿significa eso que no tengo suerte? –

Respuesta

5

No puede hacer lo que desea porque copynecesita conocer todos los parámetros posibles. Entonces, incluso si las clases de casos se heredan de Copyable, no sería el copy que necesita. Además, si va a mantener los tipos correctos, se verá frustrado por la falta de un "MyType" de Scala. Por lo tanto, no puede solo extender una clase base. Sin embargo, habría que agregar un método y tipo abstracto anotación:

abstract class BaseClass[C <: BaseClass[_]](a: String, b: Int) { 
    def setB(b0: Int): C 
    def doubleB(b0: Int) = setB(b0*2) 
} 
case class HasC(a: String, b: Int, c: Boolean) extends BaseClass[HasC](a,b) { 
    def setB(b0: Int) = this.copy(b = b0) 
    def doesStuffWithC(c0: Boolean) = doubleB(if (c0) b else -b).copy(c = c0) 
} 

Y entonces se puede:

scala> HasC("fish",1,false).doesStuffWithC(true) 
res47: HasC = HasC(fish,2,true) 

Este trabajo extra valdrá la pena si usted tiene una gran cantidad de funcionalidad compartida que depende de la capacidad de copiar solo b (ya sea muchos métodos, o una pequeña cantidad de métodos complicados); es decir, esto resuelve el problema DRY. Si por el contrario desea abstracto sobre HasC y otras clases derivadas, puede utilizar BaseClass[_] o añadir otro nivel que define setB(b0: Int): BaseBase o simplemente olvidar el tipo de parametrización y utilizar BaseClass como el tipo de retorno (pero hay que reconocer que HasC no se puede utilizar BaseClass métodos y todavía retener su identidad de tipo).

+0

Esto está cerca de lo que terminé implementando. La aplicación de la definición de la clase hija como un parámetro de tipo en la clase base es más clara que la sintaxis sugerida en la pregunta anterior sobre "Tipo de sí mismo", así que lo he incorporado, ¡gracias! –

1

Creo que estás de suerte. Los métodos copy en HasC y HasD tienen diferentes firmas. Está un poco escondido debido a los argumentos predeterminados, pero básicamente la definición en BaseClass no sabría a qué método llamar copy.

1

Podría definir un makeCopy en la clase abstracta que toma una función de copiadora que toma Unit y devuelve una BaseClass, luego, en sus métodos que la utilizan (como doubleB) anularlos en los cuerpos de la clase case y hacer uso de makeCopy pasándolo a una función anónima que hace el trabajo de crear una nueva copia con los apoyos cambió, así:

package delegatedcopy 

abstract class BaseClass(a: String, b:Int){ 
    def aField = a 
    def bField = b 
    def doubleB:BaseClass 
    def makeCopy(copier:() => BaseClass):BaseClass = copier() 
} 

case class HasC(override val aField: String, override val bField: Int, cField: Boolean) extends BaseClass(aField, bField){ 
    override def doubleB:BaseClass = makeCopy(()=> HasC(aField, bField * 2, cField)) 
} 

case class HasD(override val aField: String, override val bField: Int, dField:Double) extends BaseClass(aField, bField){ 
    override def doubleB:BaseClass = makeCopy(()=> HasD(aField, bField * 2, dField)) 
} 

una aplicación de prueba que lo demuestra:

import delegatedcopy._ 

object TestApp extends Application{ 
    val hasC = HasC("A C object", 5, true) 
    val hasD = HasD("A D object", 2, 3.55) 
    val hasCDoubleB = hasC.doubleB 
    val hasDDoubleB = hasD.doubleB 

    println(hasC) // prints HasC(A C object,5,true) 
    println(hasCDoubleB) //prints HasC(A C object,10,true) 

    println(hasD) // prints HasD(A D object,2,3.55) 
    println(hasDDoubleB) // prints HasD(A D object,4,3.55) 
} 

de esta manera, usted está capaz de mantener el método makeCopy igual para todos los niños clases como en la clase base, y probablemente puedan implementar o mezclar bastante funcionalidad en la base y las clases de casos mientras mantienen el código común en un lugar seguro y pueden pasar a los clientes una BaseClass y una coincidencia de patrón en las clases de caso específicas.

Cuestiones relacionadas