LEARN X · ЗА 15 МИН

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]
Поддержать проект