2009-12-02 18 views
27

¿Por qué es el error a continuación? ¿Cómo solucionarlo?¿Por qué una clase no puede extender rasgos con el método de la misma firma?

EDITAR: Asumí que como A y B se compilan para pares de (interfaz, clase), es una cuestión de elegir la llamada a método estático correcta para implementar al compilar C. Esperaría que la prioridad fuera según el orden.

 
scala> trait A {def hi = println("A")} 
defined trait A 

scala> trait B {def hi = println("B")} 
defined trait B 

scala> class C extends B with A 
:6: error: error overriding method hi in trait B of type => Unit; 
method hi in trait A of type => Unit needs `override' modifier 
     class C extends B with A 

scala> trait A {override def hi = println("A")} 
:4: error: method hi overrides nothing 
     trait A {override def hi = println("A")} 

EDIT: tenga en cuenta que en Ruby esto funciona así:

 
>> module B; def hi; puts 'B'; end; end 
=> nil 
>> module A; def hi; puts 'A'; end; end 
=> nil 
>> class C; include A; include B; end 
=> C 
>> c = C.new 
=> # 
>> c.hi 
B 
=> nil 

Respuesta

51

Esto funciona para mí en 2.8 y 2.11, y permitiría a ser no intrusiva en los rasgos A o B:

trait A { def hi = println("A") } 
trait B { def hi = println("B") } 

class C extends A with B { 
    override def hi = super[B].hi 
    def howdy = super[A].hi // if you still want A#hi available 
} 

object App extends Application { 
    (new C).hi // prints "B" 
} 
+3

¡Excelente! Lástima que si intento 'la clase C amplía A con B', el error no menciona esta manera de resolver el conflicto. – IttayD

+0

@IttayD con Scala 2.10.4: El mensaje de error dice cómo resolver esto: (Nota: esto se puede resolver al declarar una anulación en la clase C.) clase C extiende A con B { –

12

Se puede usar un rasgo de base común, por ejemplo Base, de la siguiente manera:

trait Base {def hi: Unit} 
trait A extends Base {override def hi = println("A")} 
trait B extends Base {override def hi = println("B")} 
class C extends A with B 

Con la jerarquía de tipos del El resultado de llamar a hi es el siguiente (tenga en cuenta el uso de {} para crear instancias de los rasgos):

scala> (new A {}).hi 
A 

scala> (new B {}).hi 
B 

scala> (new C).hi 
B 
+0

Gracias. Esperaba una solución no intrusiva. Algo que se puede hacer en C solo (como si A y B son de bibliotecas de terceros diferentes) – IttayD

1

Este es el diamond problem. ¿Qué método hi se debe heredar, el de A o el de B? Puede evitar esto como sugirió Don, al usar un rasgo base común.

+0

Vea mi ejemplo de Ruby, donde todo está bien. Es una cuestión de decidir la semántica de 'super'. Puedo sugerir que en el árbol de rasgos extendidos, se selecciona el primer DFS sabio. Tenga en cuenta también que no hay diamantes aquí. Solo A, B, C. Por el contrario, la solución es crear un rasgo Base, creando así un diamante.Entonces, un diamante es una solución, no hay problema aquí – IttayD

+4

No hay problema de diamantes en Scala, debido a la [linealización de clases] (http://jim-mcbeath.blogspot.com/2009/08/scala-class-linearization.html) ... – paradigmatic

4

Un rasgo agrega métodos a la clase que lo mezcla. Si dos rasgos agregan el mismo método, la clase terminaría con dos métodos idénticos, que, por supuesto, no pueden suceder.

Si el método es privado en el rasgo, sin embargo, no causará ningún problema. Y si desea que los métodos se superpongan entre sí, puede definir un rasgo base y luego abstract override en los rasgos heredados. Sin embargo, requiere una clase para definir el método. Aquí es un ejemplo de esto:

scala> trait Hi { def hi: Unit } 
defined trait Hi 

scala> trait A extends Hi { abstract override def hi = { println("A"); super.hi } } 
defined trait A 

scala> trait B extends Hi { abstract override def hi = { println("B"); super.hi } } 
defined trait B 

scala> class NoHi extends Hi { def hi =() } 
defined class NoHi 

scala> class C extends NoHi with B with A 
defined class C 

scala> new C().hi 
A 
B 

Sin embargo, si usted realmente quiere dos métodos separados de cada rasgo, entonces tendrá que componer en lugar de heredar .

+0

¿Por qué no simplemente hacer que el compilador seleccione un método como uno de implementación? Como A y B se compilan para la interfaz A y la clase clase A $ con métodos estáticos, entonces en la JVM está perfectamente bien que C implemente A y B y la implementación del método hi en C solo puede llamar a la clase A $ .hi – IttayD

+0

Tenga en cuenta que en su respuesta, C termina con dos (incluso tres) métodos idénticos, por lo que se puede hacer esto. – IttayD

+1

Si uno estuviera dispuesto a desechar la base conceptual de la herencia, sí, podría hacerse, y podría lamentarse más adelante. Si quieres composición, redacta, no heredes. –

0

que tenían el mismo problema y no me gustaba tener que crear un rasgo intermediario porque puedo tener 4,5 o incluso 6 rasgos con los mismos métodos, porque son rasgos que contienen operaciones CRUD (buscar, crear ...). Además, necesitaba usar esos rasgos juntos solo para fines de prueba y siempre trato de evitar tanto como sea posible modificar la estructura de mi proyecto solo para facilitar mi prueba. Así que simplemente implementar esos rasgos en diferentes objetos:

class somethingToTest { 
    object AImpl extends ATrait 
    object BImpl extends BTrait 

    val a = AImpl.methodDuplicated() 
    val b = BImpl.methodDuplicated() 
} 

probablemente no es la forma más inteligente de utilizar rasgos pero no requiere ningún cambio en el código del proyecto, que sólo implica tener un poco más código en las pruebas.

Cuestiones relacionadas