2012-06-28 30 views
6
scala> class A 
defined class A 

scala> trait B 
defined trait B 

Creación de un objeto de la clase A nos da:¿Por qué se crea una clase anónima al mezclar un rasgo?

scala> new A 
res4: A = [email protected] 

Pero la creación de un objeto de clase A con el rasgo B mixta en la que nos da:

scala> new A with B 
res3: A with B = [email protected] 

Aquí tenemos una clase anónima (insinuado por anon). Por qué ?

¿Esto se debe a que el tipo A with B se considera como un tipo nuevo (y que no se definió con un identificador anterior)?

Respuesta

13

Esto no es solo porque A with B debe considerarse como un nuevo tipo. Para el sistema de tipo Scala, no importa directamente si existe una clase correspondiente a A with B. se genera una clase anónima porque tiene que contener puente métodos para todos los métodos en los rasgos que han sido mezclados.

La razón de que una clase anónima se crea es que el objeto debe tener implementaciones de todos los métodos de A y todos los métodos desde B. En el nivel de código de byte JVM, esto garantizaría la herencia de múltiples clases, y el modelo de herencia múltiple no es compatible con la JVM.

Para simular la herencia múltiple (o composición mixin, sin embargo desea llamar), Scala hace las siguientes cosas cuando se crea un rasgo:

  1. Si el rasgo T no tiene implementaciones de métodos, se crea una interfaz que define todos los métodos en el rasgo.
  2. Si el rasgo T tiene implementaciones de métodos, también crea una clase T$class que tiene un método estático para cada uno de los métodos concretos en T. Este método estático tiene el mismo cuerpo que su método correspondiente en T, pero su firma se cambia para incluir el parámetro this.Si T tenían:

    def foo(x: Int) = x 
    

entonces T$class tendrá:

<static> def foo($this: T, x: Int) = x 

La clase obtenida por composición mixin de alguna clase A y algún rasgo T entonces tendrá un método puente especial generado que reenvía la llamada al método estático que contiene el cuerpo. De esta forma, el cuerpo del método no se duplica en todas las clases que se mezclan en T. Esta es la razón por la que debe crearse la clase anónima; debe tener métodos puente definidos para cada método en T.

Aquí hay un ejemplo. Cuando creas una nueva clase haciendo mixin composition, p. llaman new A with T:

class A { 
    def bar = println("!") 
} 

trait T { 
    def foo(x: Int) = x 
} 

new A with T 

el compilador volver a escribir más o menos a algo como esto:

class A { 
    def bar = println("!") 
} 

<interface> T { 
    def foo(x: Int): Int 
} 

class T$class { 
    <static> def foo($this: T, x: Int) = x 
} 

class $anon extends A <implements> T { 
    // notice that `bar` is inherited, but `foo` is not 
    <bridge> def foo(x: Int) = T$class.foo(this, x) 
} 
new $anon 

en cuenta que el compilador en realidad podría reescribir los callsites a foo para llamar a los métodos estáticos directamente desde el callsite, en vez que a través de un método de puente. La razón por la que no se hace de esa manera es porque ya no sería compatible con subtipar el polimorfismo.

+1

+1 especialmente para mostrar un ejemplo! –

+1

Excelente respuesta, muchas gracias. –

+0

Tenga en cuenta que al llamar 'scalac' con el argumento' -Xprint: mixin' se mostrará la construcción exacta que crea Scala. '-Xshow-phases' muestra otras fases que se pueden usar con' -Xprint: '. – outis

6

Sí. Mientras que su tipo sigue siendo A with B, tiene que haber una clase Java subyacente que implemente ambas interfaces. No hay nada de malo en eso, excepto que si crea objetos de esta manera cientos de veces, probablemente tendrá cientos de archivos de clase. En tal caso, es posible que desee crear un class AB extends A with B dedicado y luego instanciar new AB.

Como nota al margen, usted encontrará que tampoco puede crear instancias directas de los rasgos, p. new B no funcionará. También debe crear una clase explícita aquí, p. Ej. new B {}, dando como resultado una clase sintética ('anónimo').

+0

Gracias. ¿Cuál sería el número de objetos anónimos cuando una clase dedicada tiene más sentido con respecto al rendimiento? 10, 100, 1000 o más? –

+3

Solo creará una nueva clase para cada aparición de esto en su código, no para cada instancia de esta clase. Entonces, a menos que tenga 'nuevo A con B' en cientos de líneas de código, lo cual es muy poco probable, no debería haber un problema. – drexin

+0

@John - No lo sé. Yo personalmente tendría una clase en 10 casos ya; que son 10 lugares diferentes donde 'A con B' está instanciado (como @drexin dice correctamente). No tengo tales casos muy a menudo. –

Cuestiones relacionadas