Можно долго спорить, является ли возможность перегружать операторы сильной или слабой стороной конкретного языка. Но факт остается фактом — в 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
Автор: madfriend