Члены класса Scala и параметры конструктора name clash - PullRequest
20 голосов
/ 22 сентября 2010

Рассмотрим следующий класс, написанный на Java:

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

Он определяет конечное поле с именем value, которое инициализируется в конструкторе, принимая его параметр с именем alike и применяя к нему функцию.

Я хочу написать что-то подобное в Scala. Сначала я попробовал:

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

Но компилятор жалуется: ошибка: перегруженное значение метода требует тип результата

Очевидно, что компилятор считает, что выражение value внутри выражения Math.abs(value) относится к определяемому методу. Поэтому определяемый метод является рекурсивным, поэтому мне нужно указать его тип возвращаемого значения. Итак, код, который я написал, не делает то, что ожидал: я хотел, чтобы value внутри Math.abs(value) ссылался на параметр конструктора value, а не на определяемый метод. Это как если бы компилятор неявно добавил this. к Math.abs(this.value).

Добавление val или var (или private ... вариантов) к параметру конструктора, похоже, не помогает.

Итак, мой вопрос: могу ли я определить свойство с тем же именем, что и у параметра конструктора, но, возможно, с другим значением? Если так, то как? Если нет, то почему?

Спасибо!

Ответы [ 4 ]

17 голосов
/ 22 сентября 2010

Нет, вы не можете. В Scala параметры конструктора являются свойствами, поэтому нет смысла их переопределять.

Решение, естественно, заключается в использовании другого имени:

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

При таком использовании initValue не будет частью созданных экземпляров. Однако, если вы используете его в объявлении def или в сопоставлении с образцом, он становится частью каждого экземпляра класса.

4 голосов
/ 27 февраля 2013

@ Даниэль С. Собрал

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

ваш код прав, но "параметры конструктора - это свойства", это не так.

Пост с официального сайтаsaid ,

Параметр, такой как класс Foo (x: Int), превращается в поле, если на него ссылается один или несколько методов

И у Мартинаответ подтверждает свою правду:

Это все верно, но это следует рассматривать как метод реализации.Вот почему в спецификации об этом ничего не говорится.

Итак, обычно мы все еще можем рассматривать параметры первичного конструктора как параметр обычного метода, но когда на параметры ссылается какой-либо из методов, компилятор умно повернетэто в приватное поле.

Если какой-либо формальный параметр, которому предшествует val, компилятор генерирует определение получателя автоматически. Если var, дополнительно генерирует установщик.см. раздел 5 с описанием спецификаций языка.

Это все о параметрах первичного конструктора.

2 голосов
/ 22 августа 2012

Можно рассмотреть параметрическое поле

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
...

Он определяет поля и параметры с одинаковыми именами «значение», «имя».Вы можете добавить модификаторы, такие как private ...

0 голосов
/ 13 июня 2013

В случае case classes оно должно быть:

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

. Реализация copy необходима для предотвращения синтаксической версии компилятора, который будет связывать аргумент initValue.*

Я ожидаю, что компилятор достаточно умен, чтобы не сохранять «дополнительное пространство» для initValue.Я не проверял это поведение.

...