Usando Scala rasgos con los métodos implementados en Java

Supongo que no es posible invocar métodos implementados en la Scala rasgos de Java, o hay una manera?

Supongamos que tengo en Scala:

trait Trait {
  def bar = {}
}

y en Java si lo uso como

class Foo implements Trait {
}

Java se queja de que Trait is not abstract and does not override abstract method bar() in Trait

  • Es esto un error? No debería la línea de verse como «Java se queja de que Foo no es abstracto y no reemplaza método abstracto bar() en el Rasgo» ?
InformationsquelleAutor Jus12 | 2011-10-03

2 Kommentare

  1. 131

    Respuesta

    Desde la perspectiva Java Trait.scala se compila en Trait interfaz. Por lo tanto la implementación de Trait en Java es interpretado como la implementación de una interfaz que hace que los mensajes de error evidente. Respuesta corta: no se puede tomar ventaja de rasgo implementaciones en Java, ya que esto permitiría a la herencia múltiple en Java (!)

    ¿Cómo es implementado en Scala?

    Respuesta larga: entonces, ¿cómo funciona en la Scala? Mirando el bytecode generado/clases que uno puede encontrar en el siguiente código:

    interface Trait {
        void bar();
    }
    
    abstract class Trait$class {
        public static void bar(Trait thiz) {/*trait implementation*/}
    }
    
    class Foo implements Trait {
        public void bar() {
            Trait$class.bar(this);  //works because `this` implements Trait
        }
    }
    • Trait es una interfaz
    • resumen Trait$class (no confundir con Trait.class) de la clase se crea de forma transparente, que técnicamente no no implementar Trait de la interfaz. Sin embargo, sí tiene un static bar() método de tomar Trait instancia como argumento (una especie de this)
    • Foo implementa Trait interfaz
    • scalac implementa automáticamente Trait métodos delegando a Trait$class. Esto esencialmente significa llamar a la Trait$class.bar(this).

    Nota que Trait$class no es un miembro de Foo, ni Foo que se extienden. Simplemente delegados a ella pasando this.

    La mezcla en varios rasgos

    Para continuar con la digresión sobre cómo Scala funciona… dicho esto, es fácil imaginar cómo la mezcla en varios de los rasgos de las obras debajo:

    trait Trait1 {def ping(){}};
    trait Trait2 {def pong(){}};
    class Foo extends Trait1 with Trait2

    se traduce en:

    class Foo implements Trait1, Trait2 {
      public void ping() {
        Trait1$class.ping(this);    //works because `this` implements Trait1
      }
    
      public void pong() {
        Trait2$class.pong(this);    //works because `this` implements Trait2
      }
    }

    Múltiples rasgos primordiales mismo método

    Ahora es fácil imaginar cómo la mezcla en varios de los rasgos primordiales mismo método:

    trait Trait {def bar(){}};
    trait Trait1 extends Trait {override def bar(){}};
    trait Trait2 extends Trait {override def bar(){}};

    De nuevo Trait1 y Trait2 se convertirá en interfaces de extender Trait. Ahora si Trait2 viene de última hora de definir Foo:

    class Foo extends Trait1 with Trait2

    obtendrás:

    class Foo implements Trait1, Trait2 {
        public void bar() {
            Trait2$class.bar(this); //works because `this` implements Trait2
        }
    }

    Sin embargo conmutación Trait1 y Trait2 (haciendo Trait1 a ser la última) el resultado será:

    class Foo implements Trait2, Trait1 {
        public void bar() {
            Trait1$class.bar(this); //works because `this` implements Trait1
        }
    }

    Apilable modificaciones

    Ahora considerar cómo los rasgos como apilable modificaciones trabajo. Imagine tener una realmente útil a la hora de la clase Foo:

    class Foo {
      def bar = "Foo"
    }

    que desea enriquecer con algunas nuevas funcionalidades mediante rasgos:

    trait Trait1 extends Foo {
      abstract override def bar = super.bar + ", Trait1"
    }
    
    trait Trait2 extends Foo {
      abstract override def bar = super.bar + ", Trait2"
    }

    Aquí está el nuevo ‘Foo’ con esteroides:

    class FooOnSteroids extends Foo with Trait1 with Trait2

    Esto se traduce a:

    Trait1

    interface Trait1 {
      String Trait1$$super$bar();
      String bar();
    }
    abstract class Trait1$class {
      public static String bar(Trait1 thiz) {
        //interface call Trait1$$super$bar() is possible
        //since FooOnSteroids implements Trait1 (see below)
        return thiz.Trait1$$super$bar() + ", Trait1";
      }
    }

    Trait2

    public interface Trait2 {
      String Trait2$$super$bar();
      String bar();
    }
    public abstract class Trait2$class {
      public static String bar(Trait2 thiz) {
        //interface call Trait2$$super$bar() is possible
        //since FooOnSteroids implements Trait2 (see below)
        return thiz.Trait2$$super$bar() + ", Trait2";
      }
    }

    FooOnSteroids

    class FooOnSteroids extends Foo implements Trait1, Trait2 {
      public final String Trait1$$super$bar() {
        //call superclass 'bar' method version
        return Foo.bar();
      }
    
      public final String Trait2$$super$bar() {
        return Trait1$class.bar(this);
      }
    
      public String bar() {
        return Trait2$class.bar(this);
      }      
    }

    Por lo que el conjunto de la pila de invocaciones son como sigue:

    • ‘bar’ método en FooOnSteroids instancia (punto de entrada);
    • Trait2$clase del ‘bar’ método estático de pasar esto como argumento y devuelve una concatenación de Trait2$$super$bar()’ llamada de método y de la cadena «, Trait2»;
    • ‘Trait2$$super$bar()’ en FooOnSteroids instancia que se llama …
    • Trait1$clase del ‘bar’ método estático de pasar esto como argumento y devuelve una concatenación de Trait1$$super$bar()’ llamada de método y de la cadena «, Trait1»;
    • ‘Trait1$$super$bar» en FooOnSteroids instancia que se llama …
    • original Foo del ‘bar’ método

    Y el resultado es «Foo, Trait1, Trait2».

    Conclusión

    Si has conseguido leer todo, una respuesta a la pregunta original es en las primeras cuatro líneas…

    • Bueno… es que no se cómo abstract override funciona, pero es bastante bueno, de lo contrario. 🙂
    • Gracias! Y un error tipográfico: Usted escribió class Foo implements Trait1, Trait2 de nuevo cuando la intención de cambiar Trait1 y Trait2.
    • esta parte fue añadido por la Tvaroh, tal vez él puede arreglar?
    • Lo siento, no entendí la respuesta. Creo que es correcto como es. (No se dio cuenta que el fragmento de código es en realidad el código Java no Scala de código).
    • +1 Como un compilador de desarrolladores (y entusiasta), esta información fue muy útil. Ahora no necesito ingeniería inversa a mí mismo. Muchas gracias! :3
  2. 2

    Es, de hecho, no abstracto ya que bar está volviendo un vacío Unit (una especie de NOP). Probar:

    trait Trait {
      def bar: Unit
    }

    Luego bar será un Java método abstracto de regresar void.

    • Esa es mi pregunta. Si tengo un complejo método implementado en bar de Trait, me gustaría volver a utilizarlo en una clase de Java, como puedo hacer en la Scala. Pero supongo que esto no es posible. La única corrección que parece ser para convertir Rasgo en una clase abstracta.
    • Sí. Usted debe hacer es una clase abstracta. O mejor aún, mantenerlo como un rasgo, pero agregar una clase abstracta que sólo se extiende (a una Java de unión). A continuación, puede ampliar el rasgo de la Scala o heredar de la clase abstracta de Java.
    • Sí, a pesar de que este va a comer hasta el único lugar disponible para Java las clases abstractas.
    • Otra solución es utilizar composición/agregación. Usted podría utilizar el generado método estático (ver Tomasz respuesta) o escribir un objeto adecuado para agradable Java interacción.

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea