5.12. Операторы
Операторы
Groovy поддерживает большинство операторов из Java, плюс некоторые уникальные.
Арифметические операторы
Арифметические операторы в Groovy предназначены для выполнения математических операций над числами. К ним относятся:
+— сложение. Применяется к числам, а также к строкам, где он выполняет конкатенацию.-— вычитание. Используется для получения разности двух числовых значений.*— умножение. Вычисляет произведение двух чисел./— деление. Возвращает результат деления одного числа на другое. В отличие от Java, где деление целых чисел даёт целочисленный результат, Groovy всегда возвращает значение типаBigDecimalпри использовании этого оператора с целыми числами, если явно не указан другой тип.%— остаток от деления. Позволяет получить остаток после целочисленного деления одного числа на другое.
Groovy также поддерживает составные операторы присваивания, такие как +=, -=, *=, /=, %=. Эти операторы сочетают арифметическую операцию с присваиванием результата обратно переменной. Например, выражение a += 5 эквивалентно a = a + 5.
Важной особенностью является то, что арифметические операторы в Groovy можно перегружать. Это означает, что классы могут определять собственное поведение для этих операторов, что позволяет создавать интуитивно понятные API. Например, можно реализовать сложение двух объектов типа «дата» или «интервал времени», используя обычный символ +.
Операторы сравнения
Операторы сравнения используются для проверки отношений между двумя значениями. Groovy предоставляет следующие операторы:
==— равенство. В отличие от Java, где этот оператор сравнивает ссылки на объекты, в Groovy он вызывает методequals()для сравнения содержимого объектов. Это делает код более предсказуемым и соответствующим ожиданиям разработчика.!=— неравенство. Является логическим отрицанием оператора==.<— меньше.>— больше.<=— меньше или равно.>=— больше или равно.
Особое внимание заслуживает оператор <=>, известный как оператор космического корабля или пространственный оператор. Он выполняет трёхстороннее сравнение и возвращает одно из трёх возможных значений: -1, если левый операнд меньше правого; 0, если они равны; и 1, если левый операнд больше правого. Этот оператор особенно полезен при реализации сортировки или при необходимости получить информацию о порядке двух значений за одну операцию. Он вызывает метод compareTo(), если оба операнда реализуют интерфейс Comparable.
Логические операторы
Логические операторы позволяют комбинировать булевы выражения и управлять логикой программы. В Groovy поддерживаются следующие логические операторы:
&&— логическое И. Возвращаетtrue, только если оба операнда истинны. Поддерживает короткое замыкание: если первый операнд ложен, второй не вычисляется.||— логическое ИЛИ. Возвращаетtrue, если хотя бы один из операндов истинен. Также поддерживает короткое замыкание: если первый операнд истинен, второй не вычисляется.!— логическое НЕ. Инвертирует булево значение операнда.
Groovy также допускает использование словесных аналогов: and, or, not. Эти ключевые слова функционально эквивалентны символическим операторам, но иногда повышают читаемость кода, особенно в сложных условиях.
Условный (тернарный) оператор
Тернарный оператор предоставляет компактную форму записи условного выражения. Его синтаксис:
def result = condition ? trueValue : falseValue
Здесь condition — булево выражение. Если оно истинно, результатом всего выражения становится trueValue; в противном случае — falseValue. Тернарный оператор часто используется для инициализации переменных в зависимости от условия, что делает код более лаконичным по сравнению с полной конструкцией if-else.
Оператор безопасного доступа
Оператор безопасного доступа ?. решает распространённую проблему работы с объектами, которые могут быть null. Вместо того чтобы вызывать исключение при попытке доступа к свойству или методу null-объекта, этот оператор возвращает null, если левый операнд равен null. Например:
def value = object?.property
Если object равен null, выражение object?.property вернёт null, а не вызовет NullPointerException. Это особенно полезно при работе с цепочками вызовов, где любой элемент может отсутствовать. Например, person?.address?.city безопасно пройдёт по всей цепочке, возвращая null, если хотя бы один из промежуточных объектов не определён.
Оператор Elvis
Оператор Elvis ?: представляет собой сокращённую форму тернарного оператора, предназначенную для задания значения по умолчанию. Его синтаксис:
def name = input ?: "default"
Это выражение эквивалентно:
def name = input != null && input != false && input != "" ? input : "default"
Оператор Elvis возвращает левый операнд, если он считается «истинным» в контексте Groovy (то есть не null, не пустая строка, не false, не ноль и так далее). В противном случае возвращается правый операнд. Это делает код более читаемым при установке значений по умолчанию, особенно в конфигурациях или при обработке пользовательского ввода.
Операторы диапазона
Groovy предоставляет мощный и выразительный оператор диапазона .., который создаёт объект типа Range. Этот оператор позволяет задавать последовательности значений, включая как числовые, так и символьные. Например:
def numbers = 1..5 // [1, 2, 3, 4, 5]
def letters = 'a'..'d' // ['a', 'b', 'c', 'd']
Диапазоны можно использовать в циклах, условиях, а также как коллекции — они реализуют интерфейс java.util.List. Это делает их удобными для итерации, проверки принадлежности (in) и других операций, характерных для списков.
Существует также исключающий диапазон, создаваемый с помощью оператора <.., который не включает правую границу:
def exclusive = 1..<5 // [1, 2, 3, 4]
Операторы диапазона особенно полезны при работе с датами, индексами, перечислениями и другими упорядоченными типами данных, где требуется компактное описание последовательности.
Оператор индексации
Groovy расширяет стандартную индексацию массивов и коллекций, делая её доступной для любых объектов через перегрузку метода getAt() и putAt(). Оператор [] используется для получения или установки значения по ключу или индексу:
def list = [10, 20, 30]
def value = list[1] // 20
def map = [name: 'Alice', age: 30]
def name = map['name'] // 'Alice'
Более того, Groovy позволяет использовать этот оператор даже с пользовательскими классами, если они реализуют соответствующие методы. Это открывает возможности для создания DSL (Domain-Specific Languages), где поведение индексации может быть адаптировано под конкретную предметную область.
Оператор вызова метода
Оператор . в Groovy используется для вызова методов и доступа к свойствам объекта. Однако Groovy добавляет гибкость: если метод не найден во время выполнения, вызывается специальный метод methodMissing(), что позволяет динамически обрабатывать вызовы. Это лежит в основе многих метапрограммных возможностей языка.
Кроме того, Groovy поддерживает оператор *., известный как оператор распространения (spread operator). Он применяет метод ко всем элементам коллекции:
def names = ['Alice', 'Bob', 'Charlie']
def upperNames = names*.toUpperCase() // ['ALICE', 'BOB', 'CHARLIE']
Этот оператор особенно удобен при работе с коллекциями объектов, когда нужно вызвать один и тот же метод у каждого элемента без явного цикла.
Оператор объединения строк
Groovy поддерживает интерполяцию строк с помощью двойных кавычек. Внутри таких строк можно встраивать выражения, заключённые в ${}:
def name = 'Groovy'
def greeting = "Привет, ${name}!" // "Привет, Groovy!"
Это не отдельный оператор в традиционном смысле, но механизм, который делает работу со строками более естественной и читаемой. Интерполяция работает только в GString (Groovy-строках), а не в обычных Java-строках, создаваемых одинарными кавычками.
Операторы присваивания
Помимо стандартного =, Groovy поддерживает множество составных операторов присваивания: +=, -=, *=, /=, %= и другие. Эти операторы работают не только с числами, но и с коллекциями, строками и другими типами, если соответствующие методы определены.
Например, для строк:
def text = 'Hello'
text += ' World' // 'Hello World'
Для списков:
def list = [1, 2]
list += 3 // [1, 2, 3]
Такое поведение достигается за счёт перегрузки операторов на уровне методов, что делает язык гибким и интуитивным.
Операторы сравнения на идентичность
Groovy сохраняет оператор is() для проверки идентичности ссылок, аналогично Java. Однако, в отличие от Java, оператор == в Groovy не сравнивает ссылки, а вызывает equals(), что делает его более безопасным и ожидаемым в большинстве случаев. Если требуется именно сравнение по ссылке, используется метод is():
def a = new String('test')
def b = new String('test')
println a == b // true (сравнение содержимого)
println a.is(b) // false (разные объекты)
Операторы регулярных выражений
Groovy вводит удобный синтаксис для работы с регулярными выражениями с помощью оператора ~:
def pattern = ~/foo/
def matcher = 'foo bar' =~ pattern
if (matcher) {
println matcher[0] // 'foo'
}
Оператор =~ создаёт объект Matcher, который можно использовать как булево значение (указывает, найдено ли совпадение) и как список совпадений. Оператор ==~ проверяет, совпадает ли вся строка с шаблоном:
println '123' ==~ /\d+/ // true
Эти операторы значительно упрощают текстовую обработку и делают код компактным.
Перегрузка операторов
Одной из ключевых особенностей Groovy является возможность перегрузки операторов. Любой класс может определить собственное поведение для арифметических, логических и других операторов, реализуя соответствующие методы. Например:
+→plus()-→minus()*→multiply()==→equals()[]→getAt()/putAt()<<→leftShift()
Это позволяет создавать API, которые выглядят как встроенные в язык, повышая выразительность и читаемость. Например, можно реализовать сложение двух временных интервалов или конкатенацию пользовательских коллекций с помощью знакомого символа +.