2010-01-15 24 views
7

Supongamos que tengo el siguiente código:contradictorios rasgos heredados anidados

trait Trait1 { 
    trait Inner { 
    val name = "Inner1" 
    } 
} 

trait Trait2 { 
    trait Inner { 
    val name = "Inner2" 
    } 
} 

class Foo extends Trait1 with Trait2 { 
    // I want Concrete1 to be a Trait1.Inner not a Trait2.Inner 
    class Concrete1 extends Inner 
    val c = new Concrete1 
} 

object Obj { 
    def main(args: Array[String]): Unit = { 
    val foo = new Foo 
    println(foo.c.name) 
    } 
} 

Cuando mezclo en Trait1 y Trait2, en referencia a Inner parece por defecto el tipo de cualquier rasgo que mixin segundo Inner; entonces cuando llamo al Objmain metodo imprime Inner2. ¿Cómo puedo consultar Trait1.Inner en Foo? Los tres de los siguientes dar errores de compilación:

class Concrete1 extends Trait1.Inner 
class Concrete1 extends Trait1$Inner 
class Concrete1 extends Trait1#Inner 

Respuesta

6

En lugar de

class Concrete1 extends Inner 

Utilice esta

class Concrete1 extends super[Trait1].Inner 

que debería conseguirte lo que tu quieres

+0

Eso es genial, ahora puedo escribir Class Concrete1 extends super [Trait1] .Inner y class Concrete2 se extiende super [Trait2] .Inner y tiene instancias de ambos tipos de Inner en Foo. – ams

2

Por qué no para los rasgos en el orden que esperamos que tengan precedencia? La linealización de los rasgos no es arbitraria, sino específica.

+0

Sí, había pensado en hacer eso. Pero supongamos que quiero crear un Concrete2 que también extienda Trait2.Inner. ¿Entonces que? – ams

4

Hay dos espacios de nombres dentro de una plantilla (plantilla siendo el cuerpo de una clase, objeto, o rasgo.)

  1. Miembros: Vals, vars, y defs y objetos anidados
  2. Tipos: tipos alias , rasgos anidados y clases anidadas

Al heredar de varias plantillas principales, los conflictos en estos espacios de nombres se resuelven mediante la linealización de clases.

Puede reordenar su herencia para traer el Inner padre deseado a su clase, o encontrar un diseño alternativo.

+0

Gracias, no se me ocurrió que los rasgos del miembro serían anulados. ¡Tiene sentido cuando piensas en ello! – ams

3

Una opción (si puede ser invasivo para los rasgos) es definir cada característica interna como un miembro de tipo que tiene un nombre no conflictivo.

trait Trait1 { 
    type Inner1 = Inner 
    trait Inner { 
    val name = "Inner1" 
    } 
} 

trait Trait2 { 
    type Inner2 = Inner 
    trait Inner { 
    val name = "Inner2" 
    } 
} 

class Foo extends Trait1 with Trait2 { 
    class Concrete1 extends Inner1 
    class Concrete2 extends Inner2 
    val c1 = new Concrete1 
    val c2 = new Concrete2 
} 

object App extends Application { 
    val foo = new Foo 
    println(foo.c1.name) // Inner1 
    println(foo.c2.name) // Inner2 
} 

Si no puede ser invasivo para los rasgos originales (Trait1 y Trait2), se pueden ampliar para definir el tipo de miembro.

trait Trait1 { 
    trait Inner { 
    val name = "Inner1" 
    } 
} 
trait Trait2 { 
    trait Inner { 
    val name = "Inner2" 
    } 
} 

trait Trait1a extends Trait1 { 
    type Inner1 = Inner 
} 
trait Trait2a extends Trait2 { 
    type Inner2 = Inner 
} 

class Foo extends Trait1a with Trait2a { 
    class Concrete1 extends Inner1 
    class Concrete2 extends Inner2 
    val c1 = new Concrete1 
    val c2 = new Concrete2 
} 

Otro enfoque sería el uso de un rasgo intermedio para definir su primera clase concreta:

trait Trait1 { 
    trait Inner { 
    val name = "Inner1" 
    } 
} 
trait Trait2 { 
    trait Inner { 
    val name = "Inner2" 
    } 
} 

trait FooIntermediate extends Trait1 { 
    class Concrete1 extends Inner 
} 

class Foo extends FooIntermediate with Trait2 { 
    class Concrete2 extends Inner 
    val c1 = new Concrete1 
    val c2 = new Concrete2 
} 
+0

Gracias, había pensado en hacer el tipo Inner1 = Inner. Aunque no había pensado en tus otras dos opciones; ahora tengo demasiadas opciones :) – ams