Статьи

«Перегрузка оператора» в Scala

Итак, я недавно учил себя Scala, и это очень интересный язык.

Одна из приятных вещей, которые мне нравятся в этом, — это поддержка создания DSL, специфичных для домена языков. Специфичный для домена язык — или, по крайней мере, мое понимание этого — это язык, который написан специально для одной проблемной области. Одним из примеров может быть SQL, отлично подходящий для запросов реляционных баз данных, бесполезный для создания шутеров от первого лица.

Конечно, сама Scala не является DSL, это язык общего назначения. Однако он предлагает несколько функций, которые позволяют моделировать DSL, в частности перегрузку операторов и неявные преобразования. В этом посте я собираюсь сосредоточиться на первом из этих …

Перегрузка оператора

Так в чем же перегрузка операторов?

Ну, операторы, как правило, такие вещи, как + , и ! , Вы знаете те вещи, которые используете для арифметики чисел или иногда для манипулирования строками. Что ж, перегрузка операторов — так же, как перегрузка методов — позволяет вам переопределить их поведение для определенного типа и придать им значение для ваших собственных пользовательских классов.

Подожди минутку! Я уверен, что кто-то однажды сказал мне, что перегрузка оператора была злой?

Действительно, это довольно спорная тема. Некоторые считают его слишком открытым для злоупотреблений и настолько злобным в C ++, что создатели Java намеренно запретили его (за исключением «+» для конкатенации строк).

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

Допустим, вы разрабатывали класс комплексных чисел и хотите поддержать сложение. Не было бы лучше написать …

Комплексный результат = complex1 + complex2;

…скорее, чем…

Комплексный результат = complex1.add (complex2);

Первый пример гораздо более естественный, не правда ли?

То есть Scala позволяет вам перегружать операторов?

Ну не совсем. На самом деле технически совсем нет.

Так что все это просто дразнить? Это самая глупая запись в блоге, которую я когда-либо читал. Скала мусор. Я возвращаюсь к Алголу 68.

Подождите секунду, я еще не закончил. Вы видите, что Scala не поддерживает перегрузку операторов, потому что у нее нет операторов!

Скала не имеет операторов? Вы сошли с ума, я все время пишу что-то вроде «sum = 2 + 3», а как насчет всех этих забавных операций со списками? «::», и «:/». Они выглядят как операторы для меня!

Ну, это не так. Дело в том, что у Scala довольно спокойное отношение к тому, что вы можете назвать методом.

Когда ты пишешь …

сумма = 2 + 3,

… вы на самом деле вызываете метод с именем + для типа RichInt со значением 2. Вы можете даже переписать его как …

сумма = 2. + (3)

… если вы действительно этого хотите.

Ага, я понял. Так как же тогда перегрузить оператора?

Все просто, это то же самое, что написать обычный метод. Вот пример.

class Complex(val real : Double, val imag : Double) {
   
  def +(that: Complex) =
            new Complex(this.real + that.real, this.imag + that.imag)
   
  def -(that: Complex) =
            new Complex(this.real - that.real, this.imag - that.imag)
 
  override def toString = real + " + " + imag + "i"
   
}
 
object Complex {
  def main(args : Array[String]) : Unit = {
       var a = new Complex(4.0,5.0)
       var b = new Complex(2.0,3.0)
       println(a)  // 4.0 + 5.0i
       println(a + b)  // 6.0 + 8.0i
       println(a - b)  // 2.0 + 2.0i
  }
}

Хорошо, это хорошо, что если бы я хотел оператор «не», то есть что-то вроде «!»

Это унарный префиксный оператор, и да, scala может поддерживать их, хотя и более ограниченно, чем инфиксный оператор, такой как «+».

Только четыре операторы могут поддерживаться таким образом, + , , ! и ~ . Вам просто нужно вызывать ваши методы unary_! или unary_ ~ и т. д. Вот как можно добавить «~» для вычисления величины комплексного числа в нашем классе комплексных чисел.

class Complex(val real : Double, val imag : Double) {
    // ...
    def unary_~ = Math.sqrt(real * real + imag * imag)
}
 
object Complex {
  def main(args : Array[String]) : Unit = {
     var b = new Complex(2.0,3.0)
     prinln(~b) //  3.60555
   }
}

So that’s all pretty simple, but please use responsibly. Don’t create methods called «+» unless your class really does something that could be interpreted as addition. And never ever redefine the binary shift left operator «<<» as some sort of substitute for println. It’s not clever and you’ll make the Scala gods angry.

Hope you found that useful. Next up I’ll cover implicit conversions. Another nice feature of Scala that really allows you to write your code in a more natural way