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

2 Май
2012

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
По материалам Хабрахабр.



загрузка...

Комментарии:

Наверх