metodos - scala introduccion
Miembros de la clase Scala y nombre de los parĂ¡metros del constructor Choque (4)
Considere la siguiente clase escrita en Java:
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, al tomar su parámetro llamado igual y aplicarle una función.
Quiero escribir algo similar en Scala. Al principio, intenté:
class NonNegativeDouble(value: Double) {
def value = Math.abs(value)
}
Pero el compilador se queja: error: el valor del método sobrecargado necesita un tipo de resultado
Obviamente, el compilador cree que el value
expresión dentro de la expresión Math.abs(value)
refiere al método que se está definiendo. 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 referirme al value
parámetro del constructor, y no al método que se está definiendo. Es como si el compilador añadiera implícitamente this.
a Math.abs(this.value)
.
Agregar val
o var
(o variantes private ...
) al parámetro constructor no parece ayudar.
Entonces, mi pregunta es: ¿puedo definir una propiedad con el mismo nombre que un parámetro de constructor, pero tal vez un valor diferente? ¿Si es así, cómo? Si no, ¿por qué?
¡Gracias!
No, no puedes. En Scala, los parámetros del constructor son propiedades, por lo que no tiene sentido redefinirlos.
La solución, naturalmente, es usar otro nombre:
class NonNegativeDouble(initValue: Double) {
val value = Math.abs(initValue)
}
Usado así, 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.
Puedes considerar el 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". Puede agregar modificadores como privado ...
@Daniel C. Sobral
class NonNegativeDouble(initValue: Double) {
val value = Math.abs(initValue)
}
su código es correcto, pero "los parámetros del constructor son propiedades", esto no es cierto.
Una publicación del sitio oficial dijo :
Un parámetro como la clase Foo (x: Int) se convierte en un campo si se hace referencia en uno o más métodos
Y la respuesta de Martin confirma su verdad:
Eso es todo cierto, pero debe tratarse como una técnica de implementación. Es por eso que la especificación no dice nada al respecto.
Por lo tanto, normalmente podemos tratar los parámetros del constructor principal como parámetro de método normal, pero cuando los parámetros son referenciados por cualquiera de los métodos, el compilador lo convertirá ingeniosamente 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.
En el caso de las case classes
de case classes
, debería ser:
case class NonNegativeDouble(private val initValue: Double) {
val value = Math.abs(initValue)
def copy(value: Double = this.value) = NonNegativeDouble(value)
}
Se requiere la implementación de copy
para evitar la versión initValue
del compilador que initValue
argumento de initValue
.
Espero que el compilador sea lo suficientemente inteligente como para no retener el «espacio extra» para el initValue
. No he verificado este comportamiento.