Подтвердить что ты не робот

Scala члены класса и параметры конструктора name clash

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

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

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

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

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

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

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

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

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

Спасибо!

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Сообщение с официального сайта,

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

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

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

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

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

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

Ответ 3

Вы можете рассмотреть параметрическое поле

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

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

Ответ 4

В случае 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. Я не проверял это поведение.