Pull to refresh

(Перевод) Перегрузка операторов в Scala

Reading time 3 min
Views 4.5K
image

Можно долго спорить, является ли возможность перегружать операторы сильной или слабой стороной конкретного языка. Но факт остается фактом — в Scala такая возможность есть. Так почему бы её не использовать?

Материал статьи рассчитан в основном на начинающих Scala-разработчиков.

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


Итак, что же такое перегрузка операторов?
В общем случае, операторы — это давно знакомые Вам "+", "-", "*", "!" и множество других. При чем иногда один и тот же оператор может вести себя по разному в зависимости от того, чем он оперирует (например, + как взятие суммы целых чисел и + как операция конкатенации строк). Идея перегрузки операторов проста — если поведение оператора меняется в зависимости от класса объектов, с которым он работает, то почему бы не определить ему новое поведение конкретно для Вашего класса?

Подождите-ка минуту! Но мне говорили, что перегрузка операторов — это зло!

Перегрузка операторов — тема довольно противоречивая. Часто говорят, что это является причиной многих злоупотреблений, и эту возможность в С++ так оклеветали, что создатели языка Java сознательно отказались от нее (за исключением оператора "+" для конкатенации строк).

Я придерживаюсь немного другого мнения. Если подходить к перегрузке операторов с должной ответственностью, то можно извлечь существенную практическую выгоду. Приведу пример: многие объекты можно складывать вместе или суммировать, так почему бы просто не использовать оператор "+"?

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

Complex result = complex1 + complex2;
…, чем…
Complex result = complex1.add(complex2);

Первая строчка выглядит естественнее, не так ли?

Итак, Scala позволяет перегружать операторы?



Не совсем. Точнее говоря, нет.

Выходит, всё, что я прочитал до этого — ерунда? Это самая глупая статья из всех, что я читал! Ненавижу Scala. Лучше продолжу программировать на Algol 68.


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

В Scala нет операторов? Вы сошли с ума! Я столько раз писал нечто вроде «sum = 2 + 3»! А как же операции "::" и ":/" над списками? Они выглядят как операторы!


Увы, они ими не являются. Вся суть в том, что у Scala нет жестких требований к тому, что можно назвать методом.

Когда Вы пишите следующий код:

sum = 2 + 3
…, на самом деле Вы вызываете метод + в классе RichInt у экземпляра со значением 2. Можно даже переписать прошлую строчку как:
sum = 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 поддерживает и такие, правда, в более ограниченной форме, чем инфиксные операторы вроде "+".

Под ограниченной формой я подразумеваю тот факт, что унарных префиксных операторов можно переопределить только 4: "+", "-", "!" и "~". Для этого надо определить в классе соответсвующие методы 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
   }
}


В заключение

Как видно, перегружать операторы в Scala очень просто. Но пожалуйста, используйте эту возможность с умом. Не определяйте в классе методы вроде "+", если только ваш класс не умеет делать нечто, что можно интерпретировать как сложение или суммирование.

Исходная статья — Tom Jefferys, «Operator Overloading» in Scala
Tags:
Hubs:
+21
Comments 35
Comments Comments 35

Articles