Scala: метод\перегрузка оператора

Следующий пример из книги "Программирование в Scala". Учитывая класс "Rational" и следующее определение метода:

def add(that: Rational): Rational =
    new Rational(
        this.numer * that.denom + that.numer * this.denom,
        this.denom * that.denom
    )

Я могу успешно перегрузить метод add с помощью удобной версии, которая принимает аргумент Int, а использует определение выше:

def add(that: Int): Rational =
    add(new Rational(that, 1))

Пока никаких проблем.

Теперь, если я изменю имя метода на имя стиля оператора:

def +(that: Rational): Rational =
    new Rational(
        this.numer * that.denom + that.numer * this.denom,
        this.denom * that.denom
    )

И перегрузитесь так:

def +(that: Int): Rational =
    +(new Rational(that, 1))

Я получаю следующую ошибку компиляции:

(fragment of Rational.scala):19: error: value unary_+ is not a member of this.Rational
+(new Rational(that, 1))
 ^

Почему компилятор ищет унарную версию метода +?

Ответ 1

В Scala любая конструкция типа +x, -x, ~x и !x преобразуется в вызов метода x.unary_+ и т.д. Это частично разрешает Java-подобный синтаксис имеющий !b как отрицание булева b, или -x как отрицание числа x.

Следовательно, фрагмент кода +(new Rational(that, 1)) переведен в (new Rational(that,1)).unary_+, а поскольку Rational не имеет этого метода, вы получаете ошибку компиляции. Вы получите эту ошибку, только если ваша функция называется +, -, ~ или !, так как они являются единственными символами Scala, которые можно использовать как унарные операторы. Например, если вы вызываете свою функцию @+, код компилируется просто отлично.

Хотя, я бы предложил написать переопределенную функцию add как:

def +(that: Int): Rational =
  this + (new Rational(that, 1))

Этот код показывает намерение вашей функции лучше - вы добавляете новый Rational, построенный из целого числа в качестве числителя, и 1 в качестве знаменателя в this. Этот способ записи преобразуется в this.+(new Rational(that, 1)), что вам нужно - вызов функции + на this.

Обратите внимание, что вы можете использовать нотацию infix, но вызываемая функция. Например, если вы измените имя на add, вы все равно можете сохранить его как:

def add(that: Int): Rational =
  this add (new Rational(that, 1))

Ответ 2

Если вы вызываете + с явным this, он должен работать

def +(that: Int): Rational = this.+(new Rational(that, 1))

Scala позволяет определять унарные операторы, которые могут использоваться в обозначениях оператора префикса. Например, вы можете использовать + в качестве префиксного оператора для достижения того же:

def unary_+: Rational = this.+(new Rational(that, 1))
val a = new Rational(3,2)
val b = +a

Без явного this в вашем примере компилятор думает, что вы используете унарный оператор +, который не определен.

Ответ 3

Вы не указали оператор binary +, вы указали оператор унарного +.

Итак, вместо:

def +(that: Int): Rational =
  +(new Rational(that, 1))

Вам нужно написать следующее:

def +(that: Int): Rational =
  this +(new Rational(that, 1))