2009-12-11 22 views
8

En Scala, ¿cómo se define un parámetro local en el constructor primario de una clase que no es un miembro de datos y que, por ejemplo, sirve solo para inicializar un miembro de datos en la clase base?En Scala, ¿cómo se define un parámetro local en el constructor principal de una clase?

Por ejemplo, en el siguiente código, ¿cómo podría definir correctamente el parámetro b en el constructor primario de la clase B para que genere solo un parámetro local temporal y no un miembro de datos?

class A(var a: Int) 
class B(?b?) extends A(b) 

Randall, sus respuestas explicar por qué el compilador Scala queja cuando introduzco un método inc que incrementa la propiedad a, sino también cambiar el nombre del parámetro en el constructor de la clase B para que coincida con la de la parámetro en el constructor de la clase A:

class A(var a: Int) 
class B(a: Int) extends A(a) { 
    def inc(value: Int) { this.a += value } 
} 

Scala salida del compilador:

$ scala construct.scala 
construct.scala:3: error: reassignment to val 
    def inc(value: Int) { this.a += value } 
          ^
one error found 

Scala se queja porque la clase B ahora debe tener una, la propiedad privada de sólo lectura a debido a la referencia a a en inc. Cambiando B(a: Int)-B(var a: Int) genera un error de compilación diferente:

construct.scala:2: error: error overriding variable a in class A of type Int; 
variable a needs `override' modifier 
class B(var a: Int) extends A(a) { 
      ^
one error found 

Adición override no ayuda, ya sea:

construct.scala:2: error: error overriding variable a in class A of type Int; 
variable a cannot override a mutable variable 
class B(override var a: Int) extends A(a) { 
       ^
one error found 

¿Cómo puedo utilizar el mismo nombre en el parámetro en el constructor principal de B como la propiedad definida en el constructor primario de la clase base A?

Respuesta

16

Si elimina la palabra clave "var" o "val" del parámetro constructor, no genera una propiedad.

Tenga en cuenta, sin embargo, que los parámetros del constructor no var, non-val están en el alcance y accesibles en toda la clase. Si usa uno en código no constructor (es decir, en el cuerpo de un método), habrá un campo privado invisible en la clase generada que contiene ese parámetro de constructor, como si lo hubiera hecho una "var privada" o " parámetro constructor privado val ".

Adición (mejor tarde que nunca ??):

En este código las referencias al parámetro constructor sólo se producen en el cuerpo del constructor:

class C1(i: Int) { 
    val iSquared = i * i 
    val iCubed = iSquared * i 
    val iEven = i - i % 2 
} 

... Aquí el valor i existe solo durante la ejecución del constructor.

Sin embargo, en el siguiente código, porque el parámetro constructor se referencia en un cuerpo de método -que no es parte del cuerpo del constructor- el parámetro constructor debe copiarse en un campo (privado) de la clase generada (aumentando su requisito de memoria por los 4 bytes necesarios para sostener a) Int:

class C2(i: Int) { 
    val iSquared = i * i 
    val iCubed = iSquared * i 
    val iEven = i - i % 2 

    def mod(d: Int) = i % d 
} 
+0

Randall, gracias por su respuesta. ¿Puedes dar un ejemplo de tu advertencia? –

+0

Entonces, ¿no es posible tener un parámetro para el constructor sin tener ese parámetro convertido en un atributo para la instancia? Tengo una clase que recibe un valor booleano, y la inicialización varía según el valor booleano, pero no necesito el valor booleano después de que termina el constructor. Aún así, las instancias de esta clase tendrán un atributo adicional (el booleano antes mencionado) que ocupará memoria, incluso cuando no sea necesaria después de la inicialización. Eso ciertamente apesta, para un lenguaje tan grande como Scala. Triste :( – Ernesto

+1

@Ernesto, sí, como explicó Randall en su apéndice, es posible tener un parámetro constructor local, siempre que ningún método en la clase se refiera a ese parámetro. En el caso donde exista tal método, Scala "promueve" el parámetro para un miembro de datos de instancia. –

4

Después de algunos experimentos, determiné que simplemente dejando fuera var o val delante del parámetro b hará que sea un parámetro local y no un miembro de datos :

class A(var a: Int) 
class B(b: Int) extends A(b) 

expansión de Java:

$ javap -private B 
Compiled from "construct.scala" 
public class B extends A implements scala.ScalaObject{ 
    public B(int); 
} 

$ javap -private A 
Compiled from "construct.scala" 
public class A extends java.lang.Object implements scala.ScalaObject{ 
    private int a; 
    public A(int); 
    public void a_$eq(int); 
    public int a(); 
    public int $tag()  throws java.rmi.RemoteException; 
} 

en cuenta que una clase A tiene un miembro de datos privados a debido a la var a: Int en su constructor primario. Sin embargo, la clase B no tiene miembros de datos, pero su constructor primario aún tiene un único parámetro entero.

+0

Parece que si hace que B case class, b se convertirá en un miembro de datos. ¿Alguna idea de por qué? –

+0

También hice algo de experimentación, y llegué a la conclusión opuesta: incluso cuando no se prefija el parámetro con 'val' o' var', aún se conserva más allá de la construcción uctor/fase de inicialización de la instancia. De lo contrario, ¿cómo explicas que este código compila y se ejecuta? (https://gist.github.com/gnapse/96387a056f0cf45dac7a) Observe cómo se puede acceder al parámetro del constructor 'fake' dentro del método de la instancia' output'. – Ernesto

+0

@Ernesto, ¿qué versión del compilador de Scala usaste? Ejecuté esta prueba hace casi cinco años, probablemente usando la última versión en ese momento. El comportamiento puede haber cambiado desde entonces. –

3

Derek,

Si usted tiene esto:

class A(a: Int) { 
    val aa = a // reference to constructor argument in constructor code (no problem) 
    def m: Float = a.toFloat // reference to constructor argument in method body (causes a to be held in a field) 
} 

encontrará (usando javap, por ejemplo) que un campo denominado "a" está presente en la clase. Si comenta la "def m", verá que el campo no se ha creado.

4

Puede crear variables temporales a lo largo de la inicialización de miembros de la clase individuales como esto:

class A(b:Int){ 
    val m = { 
    val tmp = b*b 
    tmp+tmp 
    } 
} 
+0

Entonces 'm' es una propiedad que una instancia de la clase' A' inicializa a '2 * b * b'? –

+0

Exactamente. También puede envolver algo entre llaves dentro del constructor. Tal vez los locals instanciados allí tampoco serán creados como miembros de la clase. – ziggystar

Cuestiones relacionadas