2011-09-06 25 views
5

Acabo de tropezar con alguna situación que me parece extraña. Puede ser que me esté perdiendo lo obvio aquí, de todos modos, por favor, ayúdenme.¿Los argumentos del constructor Scala están duplicados?

Considérese la siguiente secuencia de comandos Scala repl:

scala> class X(val s: String) { def run=println("(X): "+s) } 
defined class X 

scala> class Y(s: String) extends X("MY "+s) { override def run=println("(Y): "+s) } 
defined class Y 

scala> new Y("fish").run 
(Y): fish 

En el guión que estoy definiendo una clase X con un atributo de clase "val s". Luego defino una clase Y que se supone que toma un argumento de constructor y lo pasa a X- lo cual hace. Para mostrar la diferencia, modifico "s" antes de darle a X ("MY" + s).

Finalmente creo una nueva Y y llamo "ejecutar". Esto imprime "pez" a la consola, así que obviamente el atributo "s" de la clase "X" ha sido sombreado por un nuevo atributo "s" que he creado en "Y".

Intenté esto con Scala 2.8 y 2.9.1 con el mismo resultado.

¿Se supone que es así? ¿Qué debo hacer si solo quiero pasar los argumentos del constructor de mi clase a una superclase y no quiero almacenar el parámetro yo mismo dentro de la clase secundaria? ¿Cuál es la práctica común aquí?

Gracias!

Respuesta

8

Si no usa el parámetro excepto en el constructor de la subclase, el parámetro no se almacenará. Si necesita consultar el parámetro principal y no el parámetro constructor, use un nombre de variable diferente.

clases que muestran ejemplos:

class X(val s: String) { def run=println("(X): "+s) } 
class Y(s: String) extends X("MY "+s) { override def run=println("(Y): "+s) } 
class Z(s0: String) extends X("MY "+s0) { override def run=println("(Z): "+s) } 

código de bytes que muestra falta de almacenamiento (sólo los constructores):

// Note putfield to store s 
public X(java.lang.String); 
    Code: 
    0: aload_0 
    1: aload_1 
    2: putfield #11; //Field s:Ljava/lang/String; 
    5: aload_0 
    6: invokespecial #43; //Method java/lang/Object."<init>":()V 
    9: return 

// Note putfield to store new s (then eventually calls X's constructor) 
public Y(java.lang.String); 
    Code: 
    0: aload_0 
    1: aload_1 
    2: putfield #29; //Field s:Ljava/lang/String; 
    5: aload_0 
    6: new #16; //class scala/collection/mutable/StringBuilder 
    9: dup 
    10: invokespecial #19; //Method scala/collection/mutable/StringBuilder."<init>":()V 
    13: ldC#40; //String MY 
    15: invokevirtual #25; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder; 
    18: aload_1 
    19: invokevirtual #25; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder; 
    22: invokevirtual #33; //Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String; 
    25: invokespecial #44; //Method X."<init>":(Ljava/lang/String;)V 
    28: return 

// Note - no putfield! 
public Z(java.lang.String); 
    Code: 
    0: aload_0 
    1: new #14; //class scala/collection/mutable/StringBuilder 
    4: dup 
    5: invokespecial #17; //Method scala/collection/mutable/StringBuilder."<init>":()V 
    8: ldC#39; //String MY 
    10: invokevirtual #23; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder; 
    13: aload_1 
    14: invokevirtual #23; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder; 
    17: invokevirtual #32; //Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String; 
    20: invokespecial #43; //Method X."<init>":(Ljava/lang/String;)V 
    23: return 
+0

¡Gracias por la explicación detallada! –

+0

El parámetro de clase s en Y sombrea los valores definidos en X. – mkneissl

1

En efecto, sombras el parámetro s del parámetro superclase. El punto importante es que el alcance de un parámetro de clase (es decir, un parámetro del constructor primario) es toda la clase. Entonces, en su método Y.run, no hay duda de que s se refiere a Y.s.

Eso significa que s debe mantenerse vivo en un campo, y como Rex le ha mostrado, eso es exactamente lo que está sucediendo.

Para un parámetro de constructor principal, hay tres opciones:

  • var => hace campo, getter y setter
  • val => hace que el terreno y getter
  • ni => hace campo si necesario (es decir, parámetro utilizado en los métodos), pero no getter/setter
Cuestiones relacionadas