2010-09-22 25 views
19

Considere la siguiente clase escrito en Java:Scala miembros de la clase y los parámetros del constructor nombre choque

class NonNegativeDouble { 
    private final double value; 
    public NonNegativeDouble(double value) { 
     this.value = Math.abs(value); 
    } 
    public double getValue() { return value; } 
} 

Define un campo final llamado value que se inicializa en el constructor, mediante la adopción de su parámetro llamado por igual y la aplicación de una función lo.

Quiero escribir algo similar en Scala. Al principio, he intentado:

class NonNegativeDouble(value: Double) { 
    def value = Math.abs(value) 
} 

Pero el compilador se queja: de error: sobrecarga necesidades de valor método resultan tipo

Obviamente el compilador piensa que la expresión value dentro de la expresión Math.abs(value) se refiere a que se define el método . Por lo tanto, el método que se está definiendo es recursivo, por lo que debo indicar su tipo de devolución. Entonces, el código que escribí no hace lo que esperaba: quería value dentro de Math.abs(value) para referirse al parámetro de constructor value, y no al método que se está definiendo. Es como si el compilador hubiera agregado implícitamente this. a Math.abs(this.value).

Agregar val o var (o private ... variantes) al parámetro de constructor no parece ayudar.

Entonces, mi pregunta es: ¿puedo definir una propiedad con el mismo nombre como parámetro de constructor, pero tal vez con un valor diferente? ¿Si es así, cómo? Si no, ¿por qué?

Gracias!

Respuesta

16

No, no puedes. En Scala, los parámetros del constructor son propiedades, por lo que no tiene sentido redefinirlos.

La solución, por supuesto, es utilizar otro nombre:

class NonNegativeDouble(initValue: Double) { 
    val value = Math.abs(initValue) 
} 

Se utiliza como este, initValue no será parte de las instancias creadas. Sin embargo, si lo usa en una def o una declaración de coincidencia de patrón, entonces se convierte en una parte de cada instancia de la clase.

+8

Un constructor no se hace una propiedad a menos que sea una clase de caso o anotada con val o var. Sin embargo, está disponible para su uso en cualquier lugar dentro de la definición de la clase. –

+1

¿Existe algún tipo de convención de nomenclatura de los parámetros del constructor en tales casos? –

+2

@Bruno: hay varias convenciones de nombres; A mí personalmente me gusta el que se agrega un 0 al nombre, ya que ese cero implica un punto de partida, y es breve. (A veces se anexan o anexan guiones bajos, pero personalmente siento que ya hay muchos guiones bajos). –

2

Se puede considerar campo paramétrico

class NonNegativeDouble(val value: Double, private val name: String){ 
    if (value < 0) throw new IllegalArgumentException("value cannot be negative") 
    override def toString = 
    "NonNegativeDouble(value = %s, name = %s)" format (value, name) 
} 

val tom = "Tom" 
val k = -2.3 

val a = new NonNegativeDouble(k.abs, tom) 
a: NonNegativeDouble = NonNegativeDouble(value = 2.3, name = Tom) 

a.value 
res13: Double = 2.3 

a.name 
<console>:12: error: value name in class NonNegativeDouble cannot be accessed in NonNegativeDouble 
    a.name 

val b = new NonNegativeDouble(k, tom) 
java.lang.IllegalArgumentException: value cannot be negative 
... 

Es define campos y parámetros con los mismos nombres "valor", "nombre". Se pueden añadir modificadores tales como privada ...

+1

¿Qué quiere decir con "campo paramétrico"? Y en su ejemplo, podemos instanciar fácilmente 'NonNegativeDouble', cuyo .value en realidad devolvería double negativo. – Rogach

+0

@Rogach con "campos paramétricos" puede crear campos con los mismos nombres que los nombres de parámetros en el constructor. Yo había mejorado mi ejemplo. –

4

@ Daniel C. Sobral

class NonNegativeDouble(initValue: Double) { 
    val value = Math.abs(initValue) 
} 

su código es correcto, pero "parámetros del constructor son propiedades", esto no es cierto.

A post from the official site said,

A parameter such as class Foo(x : Int) is turned into a field if it is referenced in one or more methods

Y la respuesta de Martin confirma su verdad:

That's all true, but it should be treated as an implementation technique. That's why the spec is silent about it.

Así que normalmente, todavía podemos tratar parámetros del constructor primarios como parámetro del método normal, pero cuando los parámetros se hace referencia por cualquier de los métodos, el compilador hábilmente lo convertirá en un campo privado.

Si hay algún parámetro formal precedido por el valor, el compilador genera automáticamente una definición getter. Si var, genera un setter adicionalmente. ver la sección de especificación del lenguaje 5.3.

Eso es todo acerca de los parámetros del constructor principal.

0

En el caso de case classes que debe ser:

case class NonNegativeDouble(private val initValue: Double) { 
    val value = Math.abs(initValue) 
    def copy(value: Double = this.value) = NonNegativeDouble(value) 
} 

La implementación de copy es necesario para evitar la versión sintesized del compilador que se unirá el argumento initValue.

Espero que el compilador sea lo suficientemente inteligente como para no conservar el «espacio extra» para el initValue. No he verificado este comportamiento.

Cuestiones relacionadas