Groovy
Groovy за 15 минут: переменные def, GString, замыкания, списки и мапы, switch, null-safe ?. и Elvis ?:, регулярки. Весь язык в примерах кода.
Groovy — динамический язык для платформы JVM с синтаксисом, близким к Java, но гораздо короче. Опциональная типизация, мощные замыкания, удобные коллекции и встроенная работа со строками делают его отличным «клеем» для скриптов, сборки (Gradle) и тестов. Весь язык — на одной странице, всё в комментариях кода.
Вывод
// Однострочный комментарий
/* Многострочный
комментарий */
/** Doc-комментарий для классов и методов */
println "Привет, мир!" // печать со переводом строки
print "без перевода" // печать без перевода строки
println() // пустая строка
// Скобки часто необязательны:
println 42
// Форматированный вывод (как в Java)
printf("Число: %d, текст: %s%n", 7, "ок")Переменные
def — динамический тип, выводится во время выполнения. Можно указывать и статические типы Java.
def name = "Аня" // тип определяется автоматически (String)
def age = 25 // Integer
def pi = 3.14 // BigDecimal (по умолчанию для дробных)
def ready = true // Boolean
// Динамическая типизация: переменную можно переприсвоить другим типом
def x = 10
x = "теперь строка" // ок, тип меняется
// Статические типы Java тоже работают:
int count = 5
String city = "Москва"
double rate = 0.5
long big = 10_000_000 // подчёркивания для читаемости
// Всё — объекты, даже числа:
println 42.getClass() // class java.lang.IntegerСтроки и GString
Двойные кавычки дают GString с интерполяцией $var. Одинарные — обычная строка без интерполяции.
def who = "мир"
def s1 = 'без интерполяции $who' // одинарные: выведет литерально $who
def s2 = "Привет, $who!" // двойные: подставит значение
def s3 = "Сумма: ${2 + 2}" // ${...} — любое выражение
// Многострочные строки (тройные кавычки)
def text = """Первая строка
Вторая строка
Можно с $who"""
// Полезные методы
println "Groovy".length() // 6
println "groovy".toUpperCase() // GROOVY
println " обрежь ".trim() // "обрежь"
println "a,b,c".split(",") // [a, b, c]
println "Hello"[0] // H — индексация
println "Hello"[1..3] // ell — срез по диапазону
println "ab" * 3 // ababab — повтор
println "abc".reverse() // cbaЧисла и операторы
println 7 + 3 // 10
println 7 - 3 // 4
println 7 * 3 // 21
println 7 / 3 // 2.33333333 (BigDecimal, не целочисленное!)
println 7.intdiv(3) // 2 — целочисленное деление
println 7 % 3 // 1 — остаток
println 2 ** 10 // 1024 — возведение в степень
// Сравнения и логика
println 5 > 3 && 2 < 4 // true
println 5 == 5 || false // true
println !false // true
// Спейсшип-оператор (для сравнения/сортировки): -1, 0 или 1
println 1 <=> 2 // -1
// Сокращённые присваивания
def n = 10
n += 5; n *= 2
println n // 30Условия и switch
switch в Groovy умеет сопоставлять по типу, диапазону, регулярке, списку и значению.
def age = 20
if (age >= 18) {
println "взрослый"
} else if (age >= 14) {
println "подросток"
} else {
println "ребёнок"
}
// Тернарный оператор
def status = age >= 18 ? "можно" : "нельзя"
// Мощный switch — сопоставление по разным критериям
def x = 42
switch (x) {
case 0: println "ноль"; break
case 1..10: println "от 1 до 10"; break // диапазон
case Integer: println "целое число"; break // по типу
case [42, 100]: println "из списка"; break // вхождение в список
case ~/\d+/: println "цифры (регулярка)"; break // регулярка
default: println "что-то ещё"
}Циклы
// Классический for
for (int i = 0; i < 3; i++) {
print i // 012
}
println()
// for по диапазону
for (i in 1..5) {
print i // 12345
}
println()
// each — перебор коллекции замыканием
[10, 20, 30].each { println it } // it — текущий элемент
// times — повторить N раз
3.times { println "повтор №$it" } // 0, 1, 2
// upto / downto
1.upto(3) { print it } // 123
println()
// while
def n = 3
while (n > 0) { print n; n-- } // 321Списки и мапы
Списки задаются [], мапы — [:]. Богатый набор методов высшего порядка.
// Список (ArrayList)
def nums = [1, 2, 3, 4, 5]
println nums[0] // 1
println nums[-1] // 5 — отрицательный индекс с конца
nums << 6 // добавить элемент
println nums.size() // 6
// Методы высшего порядка
println nums.collect { it * 2 } // [2, 4, 6, 8, 10, 12] — map
println nums.findAll { it % 2 == 0 } // [2, 4, 6] — фильтр
println nums.find { it > 3 } // 4 — первый подходящий
println nums.sum() // 21
println nums.max() // 6
println nums.sort { -it } // по убыванию
// Мапа (LinkedHashMap)
def user = [name: "Аня", age: 25]
println user.name // Аня — доступ по ключу
println user["age"] // 25 — доступ как по индексу
user.city = "Москва" // добавить пару
user.each { k, v -> println "$k = $v" }Замыкания (closures)
Замыкание — блок кода {} как объект. it — неявный единственный параметр.
// Замыкание без параметров
def hello = { println "Привет!" }
hello() // вызов
// С одним неявным параметром it
def square = { it * it }
println square(5) // 25
// С явными параметрами
def add = { a, b -> a + b }
println add(3, 4) // 7
// Замыкание захватывает переменные окружения
def factor = 10
def scale = { it * factor }
println scale(5) // 50
// Замыкания как аргументы (основа коллекций)
[1, 2, 3].each { println it }
// Замыкание можно передавать и сохранять
def apply = { fn, value -> fn(value) }
println apply(square, 6) // 36Функции и методы
// Метод через def — тип возврата выводится
def greet(name) {
return "Привет, $name!"
}
println greet("Аня")
// return необязателен — возвращается последнее выражение
def square(x) {
x * x
}
println square(4) // 16
// Значения по умолчанию
def power(base, exp = 2) {
base ** exp
}
println power(3) // 9 (exp=2 по умолчанию)
println power(2, 10) // 1024
// Типизированные параметры и возврат
int multiply(int a, int b) {
a * b
}
println multiply(6, 7) // 42
// Переменное число аргументов
def total(int... xs) {
xs.sum()
}
println total(1, 2, 3, 4) // 10Классы и ООП
Свойства автоматически получают геттеры/сеттеры. Доступ через точку работает «как поле».
class Person {
String name // свойство: авто getName()/setName()
int age
// Метод
String describe() {
"$name, $age лет"
}
}
// Именованные аргументы конструктора (есть из коробки)
def p = new Person(name: "Аня", age: 25)
println p.name // Аня (под капотом getName())
p.age = 26 // setAge()
println p.describe() // Аня, 26 лет
// Наследование и конструктор
class Student extends Person {
String university
String describe() {
super.describe() + ", учится в $university"
}
}
def s = new Student(name: "Иван", age: 20, university: "МГУ")
println s.describe()
// Интерфейс
interface Greeter { String greet() }null-safe ?. и Elvis ?:
def user = null
// Безопасный вызов: вернёт null вместо NullPointerException
println user?.name // null (без ошибки)
println user?.name?.length() // null — цепочка безопасна
// Elvis-оператор: значение слева, если оно "истинно", иначе справа
def name = null
println name ?: "Гость" // Гость
def list = []
println list ?: "пусто" // пусто (пустой список = false)
// Комбинация ?. и ?:
def person = [name: "Аня"]
println person?.name ?: "Аноним" // Аня
println person?.city ?: "не указан" // не указан
// Безопасное приведение/индекс
def map = [a: 1]
println map?.b ?: 0 // 0Строки и регулярные выражения
/.../ — литерал паттерна (слэши не надо экранировать). =~ ищет, ==~ проверяет полное совпадение.
// Литерал регулярки через слэши
def pattern = /\d+/
// =~ создаёт Matcher (поиск вхождений)
def m = "цена 100 руб, скидка 20" =~ /\d+/
println m[0] // 100 — первое совпадение
println m.count // 2 — сколько совпадений
// ==~ проверяет ПОЛНОЕ совпадение строки с паттерном
println "12345" ==~ /\d+/ // true
println "12a45" ==~ /\d+/ // false
// Группы захвата
def md = "2026-06-16" =~ /(\d{4})-(\d{2})-(\d{2})/
if (md.matches()) {
println md[0][1] // 2026 — год (первая группа)
}
// Замена по регулярке
println "a1b2c3".replaceAll(/\d/, "*") // a*b*c*Особенности Groovy
Truthiness, опциональные скобки и точки с запятой — то, что отличает Groovy от Java.
// Truthiness: что считается true в условиях
if ("текст") println "непустая строка = true"
if (![]) println "пустой список = false"
if (![:]) println "пустая мапа = false"
if (!0) println "ноль = false"
if (!null) println "null = false"
if (42) println "ненулевое число = true"
// Точки с запятой не нужны
def a = 1
def b = 2
// Скобки у методов часто опциональны (DSL-стиль)
println "без скобок" // вместо println("...")
// Возврат последнего выражения без return
def f() { 100 }
println f() // 100
// Оператор расширения *. — применить ко всем элементам
println([1, 2, 3]*.toString()) // [1, 2, 3] как строки
// Range как объект
def r = 1..10
println r.contains(5) // true
println (1..3).toList() // [1, 2, 3]