Магия унарных операторов в Scala (unary_)


Многие знают, что в Scala возможна перегрузка операторов.

К примеру, пусть у нас будет класс – двухмерная точка:

class Point(val x: Double, val y: Double) {

  def -(p: Point) = new Point(x-p.x, y-p.y);
  def +(p: Point) = new Point(x+p.x, y+p.y);

}

Работать с ней приятно:

   val p1 = new Point(2,2);
   val p2 = new Point(1,1);
   val p = p2 - p1;

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

   val p = new Point(2,2);
   val antiP = -p;

Конечно, вы можете попробовать сделать так:

    // ТАК ДЕЛАТЬ НЕ НАДО!
    def -() = new Point(-x,-y);

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

   val antiP = p-;

Что же делать?
Этот случай в спецификации языка Scala описан в разделе 6.12.1 Prefix Operations.
В таких ситуациях следует использовать магическую фразу “unary_”!
Она работает только для четырех операторов: -, +, ~, !.

Таким образом, дополнив нашу точку следующим методом:

class Point(val x: Double, val y: Double) {
  def unary_-() = new Point(-x,-y);
  def -(p: Point) = new Point(x-p.x, y-p.y);
  def +(p: Point) = new Point(x+p.x, y+p.y);
}

Мы можем делать следующие вещи:

   val antiP = -p;

Таким образом оператор стал префиксным.
Ради научного интереса Вы можете переопределить еще парочку операторов, например !p – получить точку симметричную относительно оси Y, ~p – относительно оси X, создав таким образом целый “зоопарк” прикольных операторов.


class Point(val x: Double, val y: Double) {

  //  !p |  p
  // ---------
  //  -p |  ~p
  def unary_-() = new Point(-x,-y);
  def unary_!() = new Point(-x,y);
  def unary_~() = new Point(x,-y);

  def -(p: Point) = new Point(x-p.x, y-p.y);
  def +(p: Point) = new Point(x+p.x, y+p.y);
}

Конечно можно еще для полноты ощущений перегрузить унарный плюс (unary_+), но это на любителя.

Что касается других операторов (кроме -,+,~,!), то на них “unary_” не действует. Вам никто не запретит сделать метод unary_*, но автоматически становится префиксным он не будет. Другими словами вызов *p – не сработает, а сработает только p.unary_*(). Думаю, это не то, что вам хотелось.
Кстати, работает как -p, так и p.unary_-(). Метод unary_-() никуда не делся, просто у нас появился еще один (более простой) способ его вызвать.

Любое использование либо копирование материалов или подборки материалов сайта, элементов дизайна и оформления допускается лишь с разрешения правообладателя и только со ссылкой на источник: programador.ru

Телеграм канал: @prgrmdr
Почта для связи: vit [at] programmisty.com