200 вопросов по Groovy


200 вопросов по Groovy

Основы языка и синтаксис

Вопрос

Что такое Groovy и для чего он используется?


Ответ

Groovy — это объектно-ориентированный язык программирования для виртуальной машины Java. Он полностью совместим с байт-кодом Java и может использоваться для разработки веб-приложений, скриптов, автоматизации, тестирования и создания конфигурационных файлов. Основное преимущество — синтаксическая выразительность и упрощение рутинных задач по сравнению с чистым Java.


Вопрос

Как объявить переменную в Groovy?


Ответ

Переменные объявляются с помощью ключевого слова def или с указанием типа:

def name = "Groovy"
String language = "Groovy"
int version = 4

Вопрос

Какие есть способы создания строк в Groovy?


Ответ

В Groovy строки создаются несколькими способами:

def singleQuote = 'Простая строка'
def doubleQuote = "Строка с интерполяцией: ${version}"
def tripleQuote = '''Многострочная строка
сохраняет форматирование'''

Вопрос

Что такое интерполяция строк в Groovy?


Ответ

Интерполяция строк позволяет вставлять значения переменных и выражений внутрь строк, заключенных в двойные кавычки:

def name = "Groovy"
def version = 4
def message = "Язык $name версии ${version + 1}"
// Результат: "Язык Groovy версии 5"

Вопрос

Как создать многострочную строку в Groovy?


Ответ

Многострочные строки создаются с помощью тройных кавычек:

def text = """Это многострочная
строка, которая сохраняет
все переносы и отступы"""

Вопрос

Какие есть логические операторы в Groovy?


Ответ

Основные логические операторы:

&& // И (конъюнкция)
|| // ИЛИ (дизъюнкция)
!  // НЕ (отрицание)
== // Равно
!= // Не равно

Вопрос

Как работают условные операторы в Groovy?


Ответ

Условные операторы включают if-else и тернарный оператор:

def x = 10
if (x > 5) {
    println "Больше 5"
} else {
    println "Меньше или равно 5"
}

// Тернарный оператор
def result = x > 5 ? "Да" : "Нет"

Вопрос

Что такое безопасная навигация (safe navigation) в Groovy?


Ответ

Безопасная навигация позволяет обращаться к свойствам объекта, который может быть null, без выбрасывания исключения:

def user = null
def name = user?.name  // Вернет null вместо ошибки

Вопрос

Как использовать оператор Elvis в Groovy?


Ответ

Оператор Elvis (?:) возвращает левый операнд, если он не является ложным значением, иначе возвращает правый:

def name = null
def displayName = name ?: "Гость"  // "Гость"

Вопрос

Какие есть циклы в Groovy?


Ответ

Основные циклы:

// for с диапазоном
for (i in 1..5) { println i }

// for с коллекцией
for (item in [1, 2, 3]) { println item }

// while
def i = 0
while (i < 5) { println i; i++ }

// each (идиоматичный способ)
[1, 2, 3].each { println it }

Вопрос

Как создать диапазон в Groovy?


Ответ

Диапазоны создаются с помощью операторов .. и ..<:

def inclusive = 1..5  // [1, 2, 3, 4, 5]
def exclusive = 1..<5 // [1, 2, 3, 4]
def chars = 'a'..'d'  // ['a', 'b', 'c', 'd']

Вопрос

Как работают операторы сравнения в Groovy?


Ответ

Операторы сравнения возвращают булевы значения:

==  // равно
!=  // не равно
>   // больше
<   // меньше
>=  // больше или равно
<=  // меньше или равно
<=> // оператор сравнения (возвращает -1, 0, 1)

Вопрос

Что такое оператор spread в Groovy?


Ответ

Оператор spread (*.) применяет метод ко всем элементам коллекции:

def names = ["Groovy", "Java", "Python"]
def lengths = names*.size()  // [6, 4, 6]

Вопрос

Как использовать оператор spread map в Groovy?


Ответ

Оператор spread map (*:) распаковывает карту в именованные параметры:

def params = [name: "Groovy", version: 4]
def create = { name, version -> "$name $version" }
def result = create(*:params)  // "Groovy 4"

Вопрос

Какие есть способы создания списков в Groovy?


Ответ

Списки создаются с помощью квадратных скобок:

def empty = []
def numbers = [1, 2, 3, 4, 5]
def mixed = [1, "two", 3.0, true]

Вопрос

Как создать карту (map) в Groovy?


Ответ

Карты создаются с помощью квадратных скобок с парами ключ-значение:

def empty = [:]
def user = [name: "John", age: 30, city: "NYC"]
def withQuotes = ["name": "John", "age": 30]

Вопрос

Как работают регулярные выражения в Groovy?


Ответ

Регулярные выражения создаются с помощью оператора ~:

def pattern = ~/^\d{3}-\d{2}-\d{4}$/
def matcher = "123-45-6789" =~ pattern
if (matcher) { println "Соответствует" }

// Поиск всех совпадений
def text = "abc123def456"
def matches = text.findAll(/\d+/)  // ["123", "456"]

Вопрос

Как использовать оператор assert в Groovy?


Ответ

Оператор assert проверяет условие и выбрасывает исключение при его нарушении:

def x = 5
assert x == 5, "x должен быть равен 5"

// Подробный вывод при ошибке
assert x > 10, "x должен быть больше 10"
// Выбросит: Assertion failed: x > 10. Expression: (x > 10). Values: x = 5

Вопрос

Что такое GString в Groovy?


Ответ

GString — это строка с интерполяцией, которая вычисляется динамически:

def count = 3
def message = "У вас ${count} сообщений"
count = 5
println message  // "У вас 3 сообщений" (вычислено при создании)

Вопрос

Как работают многострочные комментарии в Groovy?


Ответ

Многострочные комментарии заключаются между /* и */:

/*
Это многострочный
комментарий
*/

Типы данных

Вопрос

Какие примитивные типы данных есть в Groovy?


Ответ

Groovy поддерживает все примитивные типы Java:

byte, short, int, long, float, double, char, boolean

Вопрос

Как определить тип переменной в Groovy?


Ответ

Тип переменной определяется с помощью метода getClass() или оператора instanceof:

def x = 42
println x.getClass()  // class java.lang.Integer
println x instanceof Integer  // true

Вопрос

Что такое динамическая типизация в Groovy?


Ответ

Динамическая типизация позволяет переменной менять тип во время выполнения:

def x = 10
println x.getClass()  // Integer
x = "Hello"
println x.getClass()  // String

Вопрос

Как работают числа с плавающей точкой в Groovy?


Ответ

Числа с плавающей точкой создаются с десятичной точкой или суффиксом:

def floatNum = 3.14
def doubleNum = 2.71828d
def bigDecimal = 1.23G

Вопрос

Как создать символ (char) в Groovy?


Ответ

Смволы создаются с помощью одинарных кавычек:

def letter = 'A'
def digit = '5'
def space = ' '

Вопрос

Что такое замыкания (closures) в Groovy?


Ответ

Замыкания — это блоки кода, которые можно передавать как параметры и вызывать позже:

def square = { x -> x * x }
def result = square(5)  // 25

def greet = { println "Hello" }
greet()  // "Hello"

Вопрос

Как работают булевы значения в Groovy?


Ответ

Все значения могут быть интерпретированы как булевы:

// Ложные значения
null, false, 0, 0.0, '', [], [:], "".toString()

// Истинные значения
любое другое значение

Вопрос

Как создать массив в Groovy?


Ответ

Массивы создаются с помощью оператора as или явного указания типа:

def array = [1, 2, 3] as int[]
def stringArray = ["a", "b", "c"] as String[]

Вопрос

Что такое BigInteger и BigDecimal в Groovy?


Ответ

BigInteger и BigDecimal используются для точных вычислений с большими числами:

def bigInt = 12345678901234567890G
def bigDec = 3.14159265358979323846G

Вопрос

Как работают даты и время в Groovy?


Ответ

Groovy предоставляет удобные методы для работы с датами:

def now = new Date()
def tomorrow = now + 1
def nextWeek = now + 7.days
def formatted = now.format("yyyy-MM-dd")

Коллекции

Вопрос

Какие основные типы коллекций есть в Groovy?


Ответ

Основные типы коллекций:

List  // Список (упорядоченная коллекция)
Set   // Множество (уникальные элементы)
Map   // Карта (пары ключ-значение)

Вопрос

Как добавить элемент в список?


Ответ

Элементы добавляются с помощью оператора << или метода add():

def list = [1, 2, 3]
list << 4        // [1, 2, 3, 4]
list.add(5)      // [1, 2, 3, 4, 5]
list += [6, 7]   // [1, 2, 3, 4, 5, 6, 7]

Вопрос

Как получить элемент списка по индексу?


Ответ

Элементы списка получаются по индексу с помощью квадратных скобок:

def list = [10, 20, 30, 40]
def first = list[0]   // 10
def last = list[-1]   // 40 (отрицательный индекс)
def range = list[1..2] // [20, 30]

Вопрос

Как удалить элемент из списка?


Ответ

Элементы удаляются с помощью методов remove() или оператора -:

def list = [1, 2, 3, 4, 5]
list.remove(2)        // [1, 2, 4, 5]
def filtered = list - 4  // [1, 2, 5]

Вопрос

Какие есть методы для работы со списками?


Ответ

Полезные методы списков:

def list = [1, 2, 3, 4, 5]
list.size()           // 5
list.isEmpty()        // false
list.contains(3)      // true
list.reverse()        // [5, 4, 3, 2, 1]
list.sort()           // [1, 2, 3, 4, 5]
list.unique()         // удаляет дубликаты

Вопрос

Как использовать методы each, collect, find в списках?


Ответ

Эти методы работают с замыканиями:

def numbers = [1, 2, 3, 4, 5]

// each - выполняет действие для каждого элемента
numbers.each { println it }

// collect - преобразует каждый элемент
def squares = numbers.collect { it * it }  // [1, 4, 9, 16, 25]

// find - находит первый подходящий элемент
def firstEven = numbers.find { it % 2 == 0 }  // 2

Вопрос

Как работают методы findAll и any в списках?


Ответ

def numbers = [1, 2, 3, 4, 5]

// findAll - находит все подходящие элементы
def evens = numbers.findAll { it % 2 == 0 }  // [2, 4]

// any - проверяет, есть ли хотя бы один подходящий элемент
def hasEven = numbers.any { it % 2 == 0 }  // true

Вопрос

Как использовать методы every и count в списках?


Ответ

def numbers = [1, 2, 3, 4, 5]

// every - проверяет, подходят ли все элементы
def allPositive = numbers.every { it > 0 }  // true

// count - считает количество подходящих элементов
def evenCount = numbers.count { it % 2 == 0 }  // 2

Вопрос

Как работают методы sum, min, max в списках?


Ответ

def numbers = [1, 2, 3, 4, 5]

numbers.sum()     // 15
numbers.min()     // 1
numbers.max()     // 5
numbers.average() // 3.0

Вопрос

Как создать множество (Set) в Groovy?


Ответ

Множества создаются с помощью метода asSet():

def set = [1, 2, 3, 2, 1] as Set  // [1, 2, 3]
def hashSet = [1, 2, 3] as HashSet

Вопрос

Как добавить элемент в карту (Map)?


Ответ

Элементы добавляются с помощью оператора << или квадратных скобок:

def map = [name: "John", age: 30]
map << [city: "NYC"]       // [name:John, age:30, city:NYC]
map["country"] = "USA"     // добавляет новый ключ
map.put("zip", "10001")    // альтернативный способ

Вопрос

Как получить значение из карты?


Ответ

Значения получаются по ключу:

def map = [name: "John", age: 30]
def name = map["name"]     // "John"
def age = map.age          // 30 (dot-нотация)
def city = map.city ?: "Unknown"  // "Unknown"

Вопрос

Какие есть методы для работы с картами?


Ответ

Полезные методы карт:

def map = [name: "John", age: 30]

map.size()              // 2
map.isEmpty()           // false
map.containsKey("name") // true
map.containsValue(30)   // true
map.keySet()            // [name, age]
map.values()            // [John, 30]
map.entrySet()          // [name=John, age=30]

Вопрос

Как использовать методы each, collect в картах?


Ответ

def map = [name: "John", age: 30, city: "NYC"]

// each с двумя параметрами (ключ, значение)
map.each { key, value -> println "$key = $value" }

// collect с преобразованием
def upperKeys = map.collect { it.key.toUpperCase() }  // [NAME, AGE, CITY]

Вопрос

Как объединить две карты?


Ответ

Карты объединяются с помощью оператора +:

def map1 = [name: "John", age: 30]
def map2 = [city: "NYC", country: "USA"]
def merged = map1 + map2
// [name:John, age:30, city:NYC, country:USA]

Вопрос

Как отфильтровать карту?


Ответ

Фильтрация карты с помощью метода findAll:

def map = [a: 1, b: 2, c: 3, d: 4]
def filtered = map.findAll { key, value -> value > 2 }
// [c:3, d:4]

Вопрос

Как преобразовать список в карту?


Ответ

Список пар преобразуется в карту:

def pairs = [["name", "John"], ["age", 30]]
def map = pairs.collectEntries { it }
// [name:John, age:30]

Вопрос

Как использовать методы groupBy и countBy?


Ответ

def numbers = [1, 2, 3, 4, 5, 6]

// groupBy - группирует по условию
def grouped = numbers.groupBy { it % 2 }
// [1:[1, 3, 5], 0:[2, 4, 6]]

// countBy - считает по условию
def counted = numbers.countBy { it % 2 }
// [1:3, 0:3]

Вопрос

Как работают методы inject и reduce?


Ответ

def numbers = [1, 2, 3, 4, 5]

// inject - накапливает результат
def sum = numbers.inject(0) { acc, val -> acc + val }  // 15

// reduce - аналогичен inject
def product = numbers.reduce { acc, val -> acc * val }  // 120

Вопрос

Как использовать оператор in для проверки принадлежности?


Ответ

Оператор in проверяет наличие элемента в коллекции:

def list = [1, 2, 3, 4, 5]
println 3 in list      // true
println 6 in list      // false

def map = [name: "John", age: 30]
println "name" in map  // true

Функции и замыкания

Вопрос

Как объявить функцию в Groovy?


Ответ

Функции объявляются с помощью ключевого слова def:

def greet(name) {
    return "Hello, $name!"
}

def result = greet("World")  // "Hello, World!"

Вопрос

Как работают параметры по умолчанию в функциях?


Ответ

Параметры могут иметь значения по умолчанию:

def greet(name = "Guest", greeting = "Hello") {
    return "$greeting, $name!"
}

greet()              // "Hello, Guest!"
greet("John")        // "Hello, John!"
greet("John", "Hi")  // "Hi, John!"

Вопрос

Как передать переменное количество аргументов в функцию?


Ответ

Переменное количество аргументов передается с помощью оператора ...:

def sum(int... numbers) {
    return numbers.sum()
}

sum(1, 2, 3)        // 6
sum(1, 2, 3, 4, 5)  // 15

Вопрос

Что такое замыкания (closures) в Groovy?


Ответ

Замыкания — это объекты, представляющие блоки кода, которые можно передавать и вызывать:

def square = { x -> x * x }
def result = square(5)  // 25

def greet = { println "Hello" }
greet()  // "Hello"

Вопрос

Какие параметры по умолчанию есть в замыканиях?


Ответ

В замыканиях доступны параметры по умолчанию:

def list = [1, 2, 3]
list.each { println it }  // it - текущий элемент

// Явное указание параметров
list.each { item -> println item }

// Несколько параметров
[1, 2, 3].eachWithIndex { item, index -> 
    println "$index: $item" 
}

Вопрос

Как использовать делегат (delegate) в замыканиях?


Ответ

Делегат определяет контекст выполнения замыкания:

def closure = {
    println delegate.class.name
}

closure.delegate = new Date()
closure()  // java.util.Date

Вопрос

Какие есть стратегии разрешения делегата в замыканиях?


Ответ

Стратегии разрешения делегата:

Closure.DELEGATE_FIRST  // сначала делегат, потом владелец
Closure.DELEGATE_ONLY   // только делегат
Closure.OWNER_FIRST     // сначала владелец, потом делегат
Closure.OWNER_ONLY      // только владелец
Closure.TO_SELF         // только само замыкание

Вопрос

Как создать замыкание с несколькими параметрами?


Ответ

Замыкания могут принимать несколько параметров:

def multiply = { x, y -> x * y }
def result = multiply(3, 4)  // 12

def format = { firstName, lastName -> 
    "$lastName, $firstName" 
}
format("John", "Doe")  // "Doe, John"

Вопрос

Как использовать каррирование (currying) в замыканиях?


Ответ

Каррирование фиксирует часть параметров замыкания:

def multiply = { x, y -> x * y }
def timesTwo = multiply.curry(2)
def result = timesTwo(5)  // 10

def greet = { greeting, name -> "$greeting, $name!" }
def sayHello = greet.curry("Hello")
sayHello("World")  // "Hello, World!"

Вопрос

Как работают методы memoize в замыканиях?


Ответ

Методы memoize кэшируют результаты выполнения замыкания:

def expensive = { x ->
    println "Вычисление $x"
    return x * x
}.memoize()

expensive(5)  // Вычисление 5, возвращает 25
expensive(5)  // Возвращает 25 из кэша

Вопрос

Как использовать методы trampoline для рекурсии?


Ответ

Метод trampoline предотвращает переполнение стека при рекурсии:

def factorial
factorial = { n, acc = 1 ->
    if (n <= 1) acc
    else factorial.trampoline(n - 1, n * acc)
}.trampoline()

factorial(5)  // 120

Вопрос

Как создать замыкание без параметров?


Ответ

Замыкания без параметров создаются без указания параметров:

def sayHello = { -> println "Hello" }
sayHello()  // "Hello"

def getRandom = { -> Math.random() }
def number = getRandom()

Вопрос

Как использовать методы with и tap в замыканиях?


Ответ

def person = new Expando()

// with - выполняет замыкание в контексте объекта
person.with {
    name = "John"
    age = 30
}

// tap - аналогично with, но возвращает исходный объект
def result = person.tap {
    city = "NYC"
}
// result === person

Вопрос

Как работают методы every и any с замыканиями?


Ответ

def numbers = [1, 2, 3, 4, 5]

// every - все элементы удовлетворяют условию
def allPositive = numbers.every { it > 0 }  // true

// any - хотя бы один элемент удовлетворяет условию
def hasEven = numbers.any { it % 2 == 0 }  // true

Вопрос

Как использовать методы find и findAll с замыканиями?


Ответ

def numbers = [1, 2, 3, 4, 5]

// find - первый подходящий элемент
def firstEven = numbers.find { it % 2 == 0 }  // 2

// findAll - все подходящие элементы
def allEvens = numbers.findAll { it % 2 == 0 }  // [2, 4]

Вопрос

Как работают методы collect и collectMany?


Ответ

def numbers = [1, 2, 3]

// collect - преобразует каждый элемент
def squares = numbers.collect { it * it }  // [1, 4, 9]

// collectMany - преобразует и сглаживает результат
def expanded = numbers.collectMany { [it, it * 2] }  
// [1, 2, 2, 4, 3, 6]

Вопрос

Как использовать методы groupBy и countBy с замыканиями?


Ответ

def numbers = [1, 2, 3, 4, 5, 6]

// groupBy - группирует по условию
def grouped = numbers.groupBy { it % 2 }
// [1:[1, 3, 5], 0:[2, 4, 6]]

// countBy - считает по условию
def counted = numbers.countBy { it % 2 }
// [1:3, 0:3]

Вопрос

Как работают методы sort и reverse с замыканиями?


Ответ

def numbers = [3, 1, 4, 2, 5]

// sort с замыканием
def sorted = numbers.sort { a, b -> b <=> a }  // [5, 4, 3, 2, 1]

// reverse
def reversed = numbers.reverse()  // [5, 2, 4, 1, 3]

Вопрос

Как использовать методы inject и reduce с замыканиями?


Ответ

def numbers = [1, 2, 3, 4, 5]

// inject - накапливает результат
def sum = numbers.inject(0) { acc, val -> acc + val }  // 15
def product = numbers.inject(1) { acc, val -> acc * val }  // 120

// reduce - аналогичен inject
def total = numbers.reduce { acc, val -> acc + val }  // 15

Вопрос

Как работают методы take и drop в коллекциях?


Ответ

def numbers = [1, 2, 3, 4, 5]

// take - первые N элементов
def firstThree = numbers.take(3)  // [1, 2, 3]

// drop - пропускает первые N элементов
def skipThree = numbers.drop(3)   // [4, 5]

Вопрос

Как использовать методы takeWhile и dropWhile?


Ответ

def numbers = [1, 2, 3, 4, 5, 6]

// takeWhile - берет пока условие истинно
def takeEvens = numbers.takeWhile { it % 2 == 0 }  // [] (первый нечетный)

// dropWhile - пропускает пока условие истинно
def dropOdds = numbers.dropWhile { it % 2 != 0 }  // [2, 3, 4, 5, 6]

Объектно-ориентированное программирование

Вопрос

Как объявить класс в Groovy?


Ответ

Класс объявляется с помощью ключевого слова class:

class Person {
    String name
    int age
    
    def introduce() {
        return "Я ${name}, мне ${age} лет"
    }
}

def person = new Person(name: "John", age: 30)
println person.introduce()

Вопрос

Как работают свойства (properties) в классах Groovy?


Ответ

Groovy автоматически создает геттеры и сеттеры для полей класса:

class Person {
    String name  // Автоматически создает getName() и setName()
    int age      // Автоматически создает getAge() и setAge()
}

def person = new Person()
person.name = "John"  // Вызовет setName()
println person.name   // Вызовет getName()

Вопрос

Как объявить конструктор в классе Groovy?


Ответ

Конструктор объявляется как метод с именем класса:

class Person {
    String name
    int age
    
    // Конструктор без параметров
    Person() {}
    
    // Конструктор с параметрами
    Person(String name, int age) {
        this.name = name
        this.age = age
    }
}

def p1 = new Person()
def p2 = new Person("John", 30)

Вопрос

Как работает конструктор по умолчанию в Groovy?


Ответ

Groovy автоматически предоставляет конструктор по умолчанию, который принимает именованные параметры:

class Person {
    String name
    int age
}

// Все эти варианты работают
def p1 = new Person()
def p2 = new Person(name: "John")
def p3 = new Person(age: 30)
def p4 = new Person(name: "John", age: 30)

Вопрос

Как объявить метод в классе Groovy?


Ответ

Методы объявляются внутри класса:

class Calculator {
    def add(a, b) {
        return a + b
    }
    
    int multiply(int a, int b) {
        return a * b
    }
    
    void printResult(result) {
        println "Результат: $result"
    }
}

Вопрос

Как использовать наследование в Groovy?


Ответ

Наследование осуществляется с помощью ключевого слова extends:

class Animal {
    String name
    
    def speak() {
        return "Я животное"
    }
}

class Dog extends Animal {
    def speak() {
        return "Гав!"
    }
    
    def wagTail() {
        return "Хвост виляется"
    }
}

def dog = new Dog(name: "Рекс")
println dog.speak()      // "Гав!"
println dog.wagTail()    // "Хвост виляется"

Вопрос

Как работают интерфейсы в Groovy?


Ответ

Интерфейсы объявляются с помощью ключевого слова interface:

interface Flyable {
    void fly()
    String getWingspan()
}

class Bird implements Flyable {
    String name
    String wingspan = "2 метра"
    
    void fly() {
        println "$name летит"
    }
    
    String getWingspan() {
        return wingspan
    }
}

Вопрос

Как использовать абстрактные классы в Groovy?


Ответ

Абстрактные классы объявляются с помощью ключевого слова abstract:

abstract class Vehicle {
    String brand
    
    abstract void startEngine()
    
    void honk() {
        println "Бип-бип!"
    }
}

class Car extends Vehicle {
    void startEngine() {
        println "$brand заводит двигатель"
    }
}

def car = new Car(brand: "Toyota")
car.startEngine()  // "Toyota заводит двигатель"
car.honk()         // "Бип-бип!"

Вопрос

Как работают статические методы и поля в Groovy?


Ответ

Статические члены объявляются с помощью ключевого слова static:

class MathUtils {
    static final PI = 3.14159
    
    static int add(int a, int b) {
        return a + b
    }
    
    static int multiply(int a, int b) {
        return a * b
    }
}

// Вызов статических методов
println MathUtils.add(2, 3)       // 5
println MathUtils.multiply(4, 5)  // 20
println MathUtils.PI              // 3.14159

Вопрос

Как использовать ключевое слово this в Groovy?


Ответ

Ключевое слово this ссылается на текущий экземпляр класса:

class Person {
    String name
    
    Person(String name) {
        this.name = name  // this.name ссылается на поле класса
    }
    
    def introduce() {
        return "Меня зовут ${this.name}"
    }
    
    def rename(String newName) {
        this.name = newName
        return this  // Возврат текущего объекта для цепочки вызовов
    }
}

def person = new Person("John")
println person.rename("Mike").introduce()  // "Меня зовут Mike"

Вопрос

Как работают внутренние классы в Groovy?


Ответ

Внутренние классы объявляются внутри другого класса:

class OuterClass {
    private String outerField = "Внешнее поле"
    
    class InnerClass {
        def accessOuter() {
            return outerField  // Доступ к полю внешнего класса
        }
    }
}

def outer = new OuterClass()
def inner = new OuterClass.InnerClass()
println inner.accessOuter()  // "Внешнее поле"

Вопрос

Как использовать перечисления (enum) в Groovy?


Ответ

Перечисления объявляются с помощью ключевого слова enum:

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

enum Status {
    PENDING("В ожидании"),
    ACTIVE("Активен"),
    COMPLETED("Завершен")
    
    final String description
    
    Status(String description) {
        this.description = description
    }
}

def today = Day.MONDAY
println today  // MONDAY

def status = Status.ACTIVE
println status.description  // "Активен"

Вопрос

Как работают аннотации в Groovy?


Ответ

Аннотации используются для добавления метаданных к классам, методам и полям:


import groovy.transform.ToString
import groovy.transform.EqualsAndHashCode

@ToString
@EqualsAndHashCode
class Person {
    String name
    int age
}

def p1 = new Person(name: "John", age: 30)
def p2 = new Person(name: "John", age: 30)

println p1.toString()  // Person(John, 30)
println p1 == p2       // true (благодаря @EqualsAndHashCode)

Вопрос

Как использовать композицию в Groovy?


Ответ

Композиция — это использование объектов других классов в качестве полей:

class Engine {
    def start() {
        return "Двигатель запущен"
    }
}

class Car {
    Engine engine = new Engine()
    String brand
    
    def start() {
        return "$brand: ${engine.start()}"
    }
}

def car = new Car(brand: "Toyota")
println car.start()  // "Toyota: Двигатель запущен"

Вопрос

Как работают делегаты (delegates) в классах Groovy?


Ответ

Делегаты позволяют перенаправлять вызовы методов другому объекту:

class Worker {
    def work() {
        return "Работаю"
    }
}

class Manager {
    @Delegate
    Worker worker = new Worker()
    
    def manage() {
        return "Управляю"
    }
}

def manager = new Manager()
println manager.work()    // "Работаю" (делегировано в Worker)
println manager.manage()  // "Управляю"

Вопрос

Как использовать примеси (mixins) в Groovy?


Ответ

Примеси позволяют добавлять функциональность к классам:

class Flyable {
    def fly() {
        return "Лечу"
    }
}

class Bird {
    String name
}

// Применение примеси
Bird.metaClass.mixin Flyable

def bird = new Bird(name: "Орёл")
println bird.fly()  // "Лечу"

Вопрос

Как работают трейты (traits) в Groovy?


Ответ

Трейты — это композируемые единицы поведения:

trait Flyable {
    def fly() {
        return "Лечу"
    }
}

trait Swimmable {
    def swim() {
        return "Плыву"
    }
}

class Duck implements Flyable, Swimmable {
    String name
}

def duck = new Duck(name: "Утка")
println duck.fly()   // "Лечу"
println duck.swim()  // "Плыву"

Вопрос

Как использовать полиморфизм в Groovy?


Ответ

Полиморфизм позволяет объектам разных классов реагировать на одинаковые сообщения по-разному:

class Animal {
    def makeSound() {
        return "Издает звук"
    }
}

class Dog extends Animal {
    @Override
    def makeSound() {
        return "Гав!"
    }
}

class Cat extends Animal {
    @Override
    def makeSound() {
        return "Мяу!"
    }
}

def animals = [new Dog(), new Cat(), new Animal()]
animals.each { println it.makeSound() }
// Гав!
// Мяу!
// Издает звук

Вопрос

Как работают пакеты (packages) в Groovy?


Ответ

Пакеты организуют классы в пространства имен:

// Файл: com/example/Person.groovy
package com.example

class Person {
    String name
    int age
}

// Использование

import com.example.Person

def person = new Person(name: "John", age: 30)

Вопрос

Как использовать модификаторы доступа в Groovy?


Ответ

Модификаторы доступа определяют видимость членов класса:

class Example {
    public String publicField = "Публичное"
    protected String protectedField = "Защищенное"
    private String privateField = "Приватное"
    
    // По умолчанию публичный доступ
    String defaultField = "По умолчанию"
    
    // Геттер для приватного поля
    String getPrivateField() {
        return privateField
    }
}

Вопрос

Как работают вложенные интерфейсы в Groovy?


Ответ

Интерфейсы могут быть вложенными в классы:

class Outer {
    interface Callback {
        void onComplete(result)
        void onError(error)
    }
    
    def execute(Callback callback) {
        try {
            def result = "Готово"
            callback.onComplete(result)
        } catch (Exception e) {
            callback.onError(e)
        }
    }
}

// Использование
def outer = new Outer()
outer.execute(new Outer.Callback() {
    void onComplete(result) {
        println "Успех: $result"
    }
    
    void onError(error) {
        println "Ошибка: $error"
    }
})

Вопрос

Как использовать анонимные классы в Groovy?


Ответ

Анонимные классы создаются на лету:

interface Printer {
    void print(String text)
}

// Анонимный класс
def printer = new Printer() {
    void print(String text) {
        println "Печатаю: $text"
    }
}

printer.print("Привет, мир!")

Метапрограммирование

Вопрос

Что такое метапрограммирование в Groovy?


Ответ

Метапрограммирование — это программирование, которое манипулирует программой как данными. В Groovy это позволяет динамически добавлять методы и свойства к классам и объектам во время выполнения.


Вопрос

Как добавить метод к классу во время выполнения?


Ответ

Методы можно добавлять через метакласс:

class Person {
    String name
}

// Добавление метода ко всем экземплярам класса
Person.metaClass.greet = { ->
    return "Привет, я ${delegate.name}!"
}

def person = new Person(name: "John")
println person.greet()  // "Привет, я John!"

Вопрос

Как добавить метод к конкретному объекту?


Ответ

Методы можно добавлять к отдельным объектам:

class Person {
    String name
}

def person1 = new Person(name: "John")
def person2 = new Person(name: "Jane")

// Добавление метода только к одному объекту
person1.metaClass.farewell = { ->
    return "До свидания, я ${delegate.name}!"
}

println person1.farewell()  // "До свидания, я John!"
// person2.farewell() вызовет ошибку

Вопрос

Как использовать methodMissing в Groovy?


Ответ

methodMissing перехватывает вызовы несуществующих методов:

class DynamicClass {
    def methodMissing(String name, args) {
        return "Вызвали метод $name с аргументами ${args.join(', ')}"
    }
}

def obj = new DynamicClass()
println obj.nonExistentMethod(1, 2, 3)
// "Вызвали метод nonExistentMethod с аргументами 1, 2, 3"

Вопрос

Как использовать propertyMissing в Groovy?


Ответ

propertyMissing перехватывает обращения к несуществующим свойствам:

class DynamicProperties {
    def storage = [:]
    
    def propertyMissing(String name) {
        return storage[name]
    }
    
    void propertyMissing(String name, value) {
        storage[name] = value
    }
}

def obj = new DynamicProperties()
obj.firstName = "John"
obj.age = 30

println obj.firstName  // "John"
println obj.age        // 30

Вопрос

Как работают расширения классов (category) в Groovy?


Ответ

Категории временно добавляют методы к классам:

class NumberUtils {
    static def square(Number self) {
        return self * self
    }
    
    static def cube(Number self) {
        return self * self * self
    }
}

use(NumberUtils) {
    println 5.square()  // 25
    println 3.cube()    // 27
}

// Вне блока use методы недоступны
// println 5.square()  // Ошибка

Вопрос

Как использовать ExpandoMetaClass в Groovy?


Ответ

ExpandoMetaClass позволяет расширять классы:

// Включение расширяемости для всех классов
ExpandoMetaClass.enableGlobally()

class Person {
    String name
}

// Расширение класса
Person.metaClass.introduce = { ->
    "Меня зовут ${delegate.name}"
}

def person = new Person(name: "John")
println person.introduce()  // "Меня зовут John"

Вопрос

Как работают динамические свойства в Groovy?


Ответ

Динамические свойства добавляются через метакласс:

class Person {
    String name
}

def person = new Person(name: "John")

// Добавление динамического свойства
person.metaClass.age = 30
person.metaClass.city = "NYC"

println person.age   // 30
println person.city  // "NYC"

Вопрос

Как использовать respondsTo для проверки методов?


Ответ

respondsTo проверяет, существует ли метод у объекта:

class Person {
    String name
    
    def greet() {
        return "Привет!"
    }
}

def person = new Person(name: "John")

if (person.metaClass.respondsTo(person, 'greet')) {
    println person.greet()  // "Привет!"
}

if (!person.metaClass.respondsTo(person, 'nonexistent')) {
    println "Метод не существует"
}

Вопрос

Как работают замыкания с метапрограммированием?


Ответ

Замыкания используются для определения динамических методов:

class DynamicClass {
    def methods = [:]
    
    def methodMissing(String name, args) {
        if (!methods.containsKey(name)) {
            methods[name] = { ->
                "Динамический метод $name вызван"
            }
        }
        return methods[name].call()
    }
}

def obj = new DynamicClass()
println obj.firstMethod()   // "Динамический метод firstMethod вызван"
println obj.secondMethod()  // "Динамический метод secondMethod вызван"

Вопрос

Как использовать invokeMethod в Groovy?


Ответ

invokeMethod перехватывает все вызовы методов:

class Interceptor {
    def invokeMethod(String name, args) {
        println "Вызов метода: $name с аргументами ${args.join(', ')}"
        def metaMethod = metaClass.getMetaMethod(name, args)
        if (metaMethod) {
            return metaMethod.invoke(this, args)
        }
        return "Метод $name не найден"
    }
    
    def existingMethod() {
        return "Существующий метод"
    }
}

def obj = new Interceptor()
println obj.existingMethod()      // Перехват и выполнение
println obj.nonExistentMethod()   // Перехват и обработка ошибки

Вопрос

Как работают расширения стандартных классов в Groovy?


Ответ

Стандартные классы можно расширять новыми методами:

// Расширение класса String
String.metaClass.reverseWords = { ->
    delegate.split(/\s+/).reverse().join(' ')
}

println "Hello World Groovy".reverseWords()
// "Groovy World Hello"

// Расширение класса Integer
Integer.metaClass.isEven = { ->
    delegate % 2 == 0
}

println 4.isEven()   // true
println 7.isEven()   // false

Вопрос

Как использовать AST-трансформации в Groovy?


Ответ

AST-трансформации изменяют абстрактное синтаксическое дерево во время компиляции:


import groovy.transform.ToString
import groovy.transform.EqualsAndHashCode
import groovy.transform.Immutable

@ToString
@EqualsAndHashCode
@Immutable
class Point {
    int x
    int y
}

def p1 = new Point(10, 20)
def p2 = new Point(10, 20)

println p1.toString()  // Point(10, 20)
println p1 == p2       // true
// p1.x = 30  // Ошибка - класс неизменяемый

Вопрос

Как работают обработчики методов (method handlers) в Groovy?


Ответ

Обработчики методов используются для динамического создания поведения:

class MethodHandler {
    def methodCache = [:]
    
    def methodMissing(String name, args) {
        if (!methodCache.containsKey(name)) {
            methodCache[name] = { ->
                "Метод $name динамически создан"
            }
        }
        return methodCache[name].call()
    }
}

def handler = new MethodHandler()
println handler.dynamicMethod1()  // "Метод dynamicMethod1 динамически создан"
println handler.dynamicMethod2()  // "Метод dynamicMethod2 динамически создан"

Вопрос

Как использовать getMetaClass для работы с метаклассами?


Ответ

getMetaClass возвращает метакласс объекта:

class Person {
    String name
}

def person = new Person(name: "John")

def meta = person.getMetaClass()
println meta.methods.name.findAll { it.startsWith('get') }
// Список всех геттеров

// Добавление нового метода через метакласс
meta.setProperty('greet', { -> "Привет, ${delegate.name}!" })
println person.greet()  // "Привет, John!"

Вопрос

Как работают динамические делегаты в метапрограммировании?


Ответ

Динамические делегаты перенаправляют вызовы методов:

class DelegateHandler {
    def target
    
    def methodMissing(String name, args) {
        if (target && target.metaClass.respondsTo(target, name, *args)) {
            return target.invokeMethod(name, args)
        }
        return "Метод $name не найден в делегате"
    }
}

class Service {
    def process(data) {
        return "Обработано: $data"
    }
}

def handler = new DelegateHandler(target: new Service())
println handler.process("данные")  // "Обработано: данные"

Вопрос

Как использовать Expando для динамических объектов?


Ответ

Expando создает объекты с динамическими свойствами и методами:

def person = new Expando()

// Добавление свойств
person.name = "John"
person.age = 30

// Добавление методов
person.greet = { ->
    "Привет, я ${name}, мне ${age} лет"
}

println person.greet()  // "Привет, я John, мне 30 лет"

// Добавление новых свойств на лету
person.city = "NYC"
println person.city  // "NYC"

Вопрос

Как работают перехватчики свойств (property interceptors) в Groovy?


Ответ

Перехватчики свойств контролируют доступ к свойствам:

class PropertyInterceptor {
    def storage = [:]
    
    def propertyMissing(String name) {
        println "Чтение свойства: $name"
        return storage[name]
    }
    
    void propertyMissing(String name, value) {
        println "Запись свойства: $name = $value"
        storage[name] = value
    }
}

def obj = new PropertyInterceptor()
obj.firstName = "John"  // "Запись свойства: firstName = John"
println obj.firstName   // "Чтение свойства: firstName"

Вопрос

Как использовать метапрограммирование для создания строителей (builders)?


Ответ

Метапрограммирование позволяет создавать гибкие строители:

class XmlBuilder {
    def result = ''
    
    def methodMissing(String name, args) {
        def content = args[0]
        if (content instanceof Closure) {
            def builder = new XmlBuilder()
            content.delegate = builder
            content.resolveStrategy = Closure.DELEGATE_FIRST
            content.call()
            result += "<$name>${builder.result}</$name>"
        } else {
            result += "<$name>$content</$name>"
        }
        return this
    }
    
    String toString() {
        return result
    }
}

def builder = new XmlBuilder()
builder.person {
    name "John"
    age 30
    address {
        city "NYC"
        street "Main St"
    }
}

println builder.toString()
// <person><name>John</name><age>30</age><address><city>NYC</city><street>Main St</street></address></person>

Вопрос

Как работают динамические прокси в Groovy?


Ответ

Динамические прокси перехватывают вызовы методов:

class LoggingProxy {
    def target
    
    def invokeMethod(String name, args) {
        println "Вызов метода $name с аргументами ${args.join(', ')}"
        def result = target.invokeMethod(name, args)
        println "Результат: $result"
        return result
    }
}

class Calculator {
    def add(a, b) {
        return a + b
    }
}

def calc = new Calculator()
def proxy = new LoggingProxy(target: calc)

proxy.add(5, 3)
// Вызов метода add с аргументами 5, 3
// Результат: 8

Интеграция с Java

Вопрос

Как использовать классы Java в Groovy?


Ответ

Классы Java используются в Groovy без изменений:

// Использование стандартных классов Java
def list = new java.util.ArrayList()
list.add("Groovy")
list.add("Java")

def map = new java.util.HashMap()
map.put("language", "Groovy")

// Использование статических методов
def currentTime = java.lang.Система.currentTimeMillis()

Вопрос

Как вызывать методы Java из Groovy?


Ответ

Методы Java вызываются так же, как методы Groovy:

// Вызов методов экземпляра
def string = "Hello"
def length = string.length()  // 5
def upper = string.toUpperCase()  // "HELLO"

// Вызов статических методов
def max = java.lang.Math.max(10, 20)  // 20
def random = java.lang.Math.random()

Вопрос

Как работать с исключениями Java в Groovy?


Ответ

Исключения Java обрабатываются стандартными механизмами:

try {
    def number = Integer.parseInt("abc")
} catch (NumberFormatException e) {
    println "Ошибка преобразования: ${e.message}"
}

// Проверяемые и непроверяемые исключения
try {
    new java.io.FileInputStream("nonexistent.txt")
} catch (java.io.FileNotFoundException e) {
    println "Файл не найден"
}

Вопрос

Как использовать дженерики Java в Groovy?


Ответ

Дженерики работают так же, как в Java:

// Явное указание типов
List<String> stringList = new ArrayList<>()
stringList.add("Groovy")
stringList.add("Java")

Map<String, Integer> map = new HashMap<>()
map.put("one", 1)
map.put("two", 2)

// Автоматическое определение типов
def numbers = [1, 2, 3] as List<Integer>
def names = ["John", "Jane"] as List<String>

Вопрос

Как использовать аннотации Java в Groovy?


Ответ

Аннотации Java применяются к классам и методам Groovy:


import java.lang.Deprecated
import java.lang.Override

class MyClass {
    @Deprecated
    def oldMethod() {
        return "Старый метод"
    }
    
    @Override
    String toString() {
        return "MyClass"
    }
}

Вопрос

Как работать с перечислениями Java в Groovy?


Ответ

Перечисления Java используются напрямую:

// Использование стандартных перечислений
def day = java.util.concurrent.TimeUnit.SECONDS
println day.name()  // "SECONDS"

// Сравнение
if (day == java.util.concurrent.TimeUnit.SECONDS) {
    println "Это секунды"
}

// Итерация
java.util.concurrent.TimeUnit.values().each { unit ->
    println unit
}

Вопрос

Как использовать интерфейсы Java в Groovy?


Ответ

Интерфейсы Java реализуются в классах Groovy:

// Реализация интерфейса
class MyList implements java.util.List {
    private List delegate = []
    
    boolean add(Object o) {
        delegate.add(o)
    }
    
    Object get(int index) {
        delegate.get(index)
    }
    
    int size() {
        delegate.size()
    }
    
    // Реализация остальных методов...
}

def list = new MyList()
list.add("Groovy")
println list.get(0)  // "Groovy"

Вопрос

Как работать с коллекциями Java в Groovy?


Ответ

Коллекции Java интегрируются с методами Groovy:

// Создание коллекций Java
def javaList = new java.util.ArrayList()
javaList.add("A")
javaList.add("B")

// Использование методов Groovy
def groovyStyle = javaList.collect { it.toLowerCase() }
println groovyStyle  // ["a", "b"]

// Фильтрация
def filtered = javaList.findAll { it == "A" }
println filtered  // ["A"]

Вопрос

Как использовать потоки (streams) Java 8 в Groovy?


Ответ

Потоки Java 8 работают в Groovy:

def numbers = [1, 2, 3, 4, 5] as Integer[]

def result = numbers.stream()
    .filter { it % 2 == 0 }
    .map { it * 2 }
    .collect(java.util.stream.Collectors.toList())

println result  // [4, 8]

Вопрос

Как работать с лямбда-выражениями Java в Groovy?


Ответ

Лямбда-выражения совместимы с интерфейсами функциональных интерфейсов:


import java.util.function.Function

// Использование лямбды
Function<String, Integer> lengthFunction = { String s -> s.length() }

def result = lengthFunction.apply("Groovy")
println result  // 6

// Передача в методы Java
def list = ["apple", "banana", "cherry"]
list.sort(Comparator.comparing({ it.length() }))
println list  // ["apple", "cherry", "banana"]

Вопрос

Как использовать рефлексию Java в Groovy?


Ответ

Рефлексия работает так же, как в Java:

class MyClass {
    private String secret = "Секрет"
    public String publicField = "Публичное"
}

def obj = new MyClass()

// Получение полей через рефлексию
def field = obj.class.getDeclaredField("secret")
field.setAccessible(true)
println field.get(obj)  // "Секрет"

// Получение методов
def methods = obj.class.methods
methods.each { println it.name }

Вопрос

Как работать с аннотациями времени выполнения в Groovy?


Ответ

Аннотации времени выполнения доступны через рефлексию:


import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String value()
}

@MyAnnotation("Тест")
class AnnotatedClass {
}

// Чтение аннотации
def annotation = AnnotatedClass.getAnnotation(MyAnnotation)
println annotation.value()  // "Тест"

Вопрос

Как использовать классы из пакета java.time в Groovy?


Ответ

Классы java.time используются напрямую:


import java.time.*

// LocalDate
def today = LocalDate.now()
println today  // 2026-03-27

// LocalTime
def now = LocalTime.now()
println now

// LocalDateTime
def dateTime = LocalDateTime.now()
println dateTime

// Period и Duration
def period = Period.between(LocalDate.of(2020, 1, 1), LocalDate.now())
println period.years  // Количество лет

def duration = Duration.between(LocalTime.of(10, 0), LocalTime.now())
println duration.toMinutes()  // Количество минут

Вопрос

Как работать с файлами и потоками ввода-вывода Java?


Ответ

Файловые операции используют классы java.io:


import java.io.*

// Чтение файла
def file = new File("test.txt")
def reader = new BufferedReader(new FileReader(file))
def line = reader.readLine()
reader.close()

// Запись в файл
def writer = new BufferedWriter(new FileWriter("output.txt"))
writer.write("Hello, Groovy!")
writer.close()

// Использование try-with-resources
new BufferedReader(new FileReader("test.txt")).withCloseable { br ->
    println br.readLine()
}

Вопрос

Как использовать многопоточность Java в Groovy?


Ответ

Многопоточность реализуется через классы java.lang.Thread:

// Создание потока
def thread = new Thread({
    println "Поток запущен"
    Thread.sleep(1000)
    println "Поток завершен"
})

thread.start()
thread.join()

// Использование Runnable
def runnable = {
    println "Runnable выполняется"
} as Runnable

new Thread(runnable).start()

Вопрос

Как работать с сокетами в Groovy?


Ответ

Сокеты используют классы java.net:


import java.net.*

// TCP клиент
def socket = new Socket("example.com", 80)
def output = new PrintWriter(socket.getOutputStream(), true)
def input = new BufferedReader(new InputStreamReader(socket.getInputStream()))

output.println("GET / HTTP/1.1")
output.println("Host: example.com")
output.println()
output.println()

println input.readLine()
socket.close()

Вопрос

Как использовать классы из java.util.concurrent в Groovy?


Ответ

Конкурентные утилиты работают напрямую:


import java.util.concurrent.*

// ExecutorService
def executor = Executors.newFixedThreadPool(2)
executor.submit({ println "Задача 1" } as Runnable)
executor.submit({ println "Задача 2" } as Runnable)
executor.shutdown()

// Future
def future = executor.submit({ 42 } as Callable)
println future.get()  // 42

// ConcurrentHashMap
def map = new ConcurrentHashMap<>()
map.put("key", "value")
println map.get("key")  // "value"

Вопрос

Как работать с сериализацией в Groovy?


Ответ

Сериализация использует интерфейс java.io.Serializable:


import java.io.*

class Person implements Serializable {
    String name
    int age
    transient String password  // Не сериализуется
}

def person = new Person(name: "John", age: 30, password: "secret")

// Сериализация
def file = new File("person.ser")
def oos = new ObjectOutputStream(new FileOutputStream(file))
oos.writeObject(person)
oos.close()

// Десериализация
def ois = new ObjectInputStream(new FileInputStream(file))
def loaded = ois.readObject()
ois.close()

println loaded.name  // "John"
println loaded.age   // 30

Вопрос

Как использовать классы из java.nio в Groovy?


Ответ

NIO классы предоставляют современный ввод-вывод:


import java.nio.*
import java.nio.file.*

// Чтение файла
def path = Paths.get("test.txt")
def content = Files.readAllBytes(path)
def text = new String(content, "UTF-8")
println text

// Запись в файл
Files.write(Paths.get("output.txt"), "Hello".getBytes())

// WatchService для мониторинга изменений
def watcher = FileSystems.getDefault().newWatchService()
Paths.get(".").register(watcher, StandardWatchEventKinds.ENTRY_MODIFY)

Вопрос

Как работать с JDBC в Groovy?


Ответ

JDBC используется для работы с базами данных:


import java.sql.*

// Загрузка драйвера
Class.forName("org.h2.Driver")

// Создание соединения
def conn = DriverManager.getConnection("jdbc:h2:mem:test", "sa", "")

// Выполнение запроса
def stmt = conn.createStatement()
stmt.executeUpdate("CREATE TABLE users (id INT, name VARCHAR(50))")
stmt.executeUpdate("INSERT INTO users VALUES (1, 'John')")

// Чтение данных
def rs = stmt.executeQuery("SELECT * FROM users")
while (rs.next()) {
    println "${rs.getInt('id')}: ${rs.getString('name')}"
}

rs.close()
stmt.close()
conn.close()

Работа с файлами и вводом-выводом

Вопрос

Как читать файл в Groovy?


Ответ

Файлы читаются с помощью класса File и его методов:

// Чтение всего файла как строки
def content = new File("example.txt").text

// Чтение построчно
new File("example.txt").eachLine { line ->
    println line
}

// Чтение как список строк
def lines = new File("example.txt").readLines()

// Чтение с указанием кодировки
def contentUTF8 = new File("example.txt").getText("UTF-8")

Вопрос

Как записать данные в файл в Groovy?


Ответ

Запись в файл осуществляется несколькими способами:

def file = new File("output.txt")

// Запись строки (перезаписывает файл)
file.text = "Hello, Groovy!"

// Добавление в конец файла
file.append("Новая строка\n")

// Запись списка строк
file.write(["Line 1", "Line 2", "Line 3"].join("\n"))

// Запись с указанием кодировки
file.setText("Текст на русском", "UTF-8")

Вопрос

Как проверить существование файла или директории?


Ответ

Проверка существования выполняется с помощью методов exists(), isFile() и isDirectory():

def file = new File("example.txt")
def dir = new File("mydir")

if (file.exists()) {
    println "Файл существует"
}

if (dir.isDirectory()) {
    println "Это директория"
}

if (!file.isFile()) {
    println "Это не файл"
}

Вопрос

Как создать директорию в Groovy?


Ответ

Директории создаются с помощью методов mkdir() и mkdirs():

def dir = new File("newdir")
dir.mkdir()  // Создает одну директорию

def nestedDir = new File("parent/child/grandchild")
nestedDir.mkdirs()  // Создает всю цепочку директорий

Вопрос

Как получить список файлов в директории?


Ответ

Список файлов получается с помощью методов listFiles() и eachFile():

def dir = new File(".")

// Получение массива файлов
def files = dir.listFiles()

// Итерация по файлам
dir.eachFile { file ->
    println file.name
}

// Фильтрация файлов
dir.eachFileMatch(~/.*\.txt$/) { file ->
    println "Текстовый файл: ${file.name}"
}

// Рекурсивный обход
dir.eachFileRecurse { file ->
    println file.absolutePath
}

Вопрос

Как удалить файл или директорию?


Ответ

Удаление выполняется с помощью методов delete() и deleteDir():

def file = new File("temp.txt")
file.delete()  // Удаляет файл

def dir = new File("tempdir")
dir.deleteDir()  // Удаляет директорию со всем содержимым

// Безопасное удаление
if (file.exists()) {
    file.delete()
}

Вопрос

Как получить информацию о файле?


Ответ

Информация о файле доступна через различные методы:

def file = new File("example.txt")

println "Имя: ${file.name}"
println "Путь: ${file.path}"
println "Абсолютный путь: ${file.absolutePath}"
println "Размер: ${file.length()} байт"
println "Последнее изменение: ${new Date(file.lastModified())}"
println "Можно читать: ${file.canRead()}"
println "Можно писать: ${file.canWrite()}"
println "Скрытый: ${file.isHidden()}"

Вопрос

Как работать с путями в Groovy?


Ответ

Пути обрабатываются с помощью операторов и методов:

// Объединение путей
def path = new File("parent") / "child" / "file.txt"

// Получение родительской директории
def parent = new File("path/to/file.txt").parentFile

// Получение расширения файла
def extension = new File("document.pdf").name.split("\\.")[-1]

// Нормализация пути
def normalized = new File("a/../b/c").canonicalFile

Вопрос

Как использовать временные файлы в Groovy?


Ответ

Временные файлы создаются с помощью статических методов:

// Временный файл
def tempFile = File.createTempFile("prefix", ".tmp")
tempFile.deleteOnExit()  // Автоматическое удаление при выходе

// Временная директория
def tempDir = File.createTempFile("dir", "")
tempDir.delete()
tempDir.mkdir()
tempDir.deleteOnExit()

Вопрос

Как копировать файлы в Groovy?


Ответ

Копирование файлов выполняется с помощью метода withInputStream и withOutputStream:

def source = new File("source.txt")
def destination = new File("destination.txt")

destination.withOutputStream { out ->
    source.withInputStream { inStream ->
        out << inStream
    }
}

// Альтернативный способ
destination.bytes = source.bytes

Вопрос

Как переименовать файл в Groovy?


Ответ

Переименование выполняется с помощью метода renameTo():

def oldFile = new File("oldname.txt")
def newFile = new File("newname.txt")

if (oldFile.renameTo(newFile)) {
    println "Файл переименован успешно"
} else {
    println "Не удалось переименовать файл"
}

Вопрос

Как работать с двоичными файлами в Groovy?


Ответ

Двоичные файлы обрабатываются через байтовые массивы:

// Чтение двоичного файла
def bytes = new File("image.jpg").bytes

// Запись двоичных данных
def outputFile = new File("copy.jpg")
outputFile.bytes = bytes

// Постраничное чтение больших файлов
def buffer = new byte[8192]
new File("large.bin").withInputStream { input ->
    def output = new FileOutputStream("copy.bin")
    int bytesRead
    while ((bytesRead = input.read(buffer)) != -1) {
        output.write(buffer, 0, bytesRead)
    }
    output.close()
}

Вопрос

Как использовать замыкания для работы с потоками?


Ответ

Замыкания обеспечивают автоматическое закрытие потоков:

// Работа с InputStream
new File("input.txt").withInputStream { stream ->
    // Обработка потока
    def data = stream.read()
}

// Работа с OutputStream
new File("output.txt").withOutputStream { stream ->
    stream.write("Hello".getBytes())
}

// Работа с Reader/Writer
new File("text.txt").withReader { reader ->
    def line = reader.readLine()
}

new File("output.txt").withWriter { writer ->
    writer.writeLine("Hello, World!")
}

Вопрос

Как обрабатывать ошибки при работе с файлами?


Ответ

Ошибки обрабатываются с помощью блоков try-catch:

try {
    def content = new File("nonexistent.txt").text
} catch (FileNotFoundException e) {
    println "Файл не найден: ${e.message}"
} catch (IOException e) {
    println "Ошибка ввода-вывода: ${e.message}"
}

// Безопасное чтение
def safeRead = { filename ->
    try {
        return new File(filename).text
    } catch (Exception e) {
        return null
    }
}

Вопрос

Как фильтровать файлы по критериям?


Ответ

Фильтрация выполняется с помощью замыканий:

def dir = new File(".")

// Файлы больше 1KB
def largeFiles = dir.listFiles().findAll { it.length() > 1024 }

// Текстовые файлы
def textFiles = dir.listFiles().findAll { 
    it.isFile() && it.name.endsWith(".txt") 
}

// Файлы, измененные сегодня
def today = new Date().clearTime()
def recentFiles = dir.listFiles().findAll { 
    new Date(it.lastModified()).clearTime() == today 
}

Вопрос

Как получить свободное место на диске?


Ответ

Информация о дисковом пространстве доступна через методы:

def file = new File("/")

println "Свободно: ${file.freeSpace} байт"
println "Общее: ${file.totalSpace} байт"
println "Доступно: ${file.usableSpace} байт"

// Процент использования
def usedPercent = (1 - (file.freeSpace / file.totalSpace)) * 100
println "Используется: ${usedPercent.round(2)}%"

Вопрос

Как работать с архивами ZIP в Groovy?


Ответ

Архивы ZIP обрабатываются с помощью классов из java.util.zip:


import java.util.zip.*

// Создание ZIP архива
def zipFile = new File("archive.zip")
def zipOut = new ZipOutputStream(new FileOutputStream(zipFile))

new File("source").eachFileRecurse { file ->
    def entryName = file.path.replace("source/", "")
    zipOut.putNextEntry(new ZipEntry(entryName))
    zipOut.write(file.bytes)
    zipOut.closeEntry()
}

zipOut.close()

// Извлечение ZIP архива
def zipIn = new ZipInputStream(new FileInputStream("archive.zip"))
ZipEntry entry
while ((entry = zipIn.getNextEntry()) != null) {
    def outputFile = new File("extracted/" + entry.name)
    if (entry.directory) {
        outputFile.mkdirs()
    } else {
        outputFile.parentFile.mkdirs()
        outputFile.bytes = zipIn.getBytes()
    }
    zipIn.closeEntry()
}
zipIn.close()

Вопрос

Как использовать glob-паттерны для поиска файлов?


Ответ

Glob-паттерны используются с методом eachFileMatch:

def dir = new File(".")

// Все .txt файлы
dir.eachFileMatch(~/.*\.txt$/) { file ->
    println "Текстовый файл: ${file.name}"
}

// Файлы с определенным шаблоном
dir.eachFileMatch(~/data_\d{4}\.csv$/) { file ->
    println "CSV файл: ${file.name}"
}

// Рекурсивный поиск
dir.eachFileRecurse(groovy.io.FileType.FILES) { file ->
    if (file.name ==~ /.*\.log$/) {
        println "Лог файл: ${file.absolutePath}"
    }
}

Вопрос

Как мониторить изменения файлов в Groovy?


Ответ

Мониторинг изменений реализуется через циклы или Java NIO:

// Простой мониторинг через опрос
def file = new File("watched.txt")
def lastModified = file.lastModified()

while (true) {
    if (file.lastModified() != lastModified) {
        println "Файл изменен!"
        lastModified = file.lastModified()
    }
    Thread.sleep(1000)
}

// Использование Java NIO WatchService (более эффективно)

import java.nio.file.*

def watcher = FileSystems.getDefault().newWatchService()
Paths.get(".").register(watcher, StandardWatchEventKinds.ENTRY_MODIFY)

while (true) {
    def key = watcher.take()
    key.pollEvents().each { event ->
        println "Изменен: ${event.context()}"
    }
    key.reset()
}

Вопрос

Как работать с символическими ссылками в Groovy?


Ответ

Смволические ссылки обрабатываются через Java NIO:


import java.nio.file.*

def link = Paths.get("symlink")
def target = Paths.get("target.txt")

// Создание символической ссылки
Files.createSymbolicLink(link, target)

// Проверка, является ли путь символической ссылкой
if (Files.isSymbolicLink(link)) {
    println "Это символическая ссылка"
    def actualTarget = Files.readSymbolicLink(link)
    println "Целевой файл: $actualTarget"
}

Скрипты и командная строка

Вопрос

Как создать исполняемый скрипт на Groovy?


Ответ

Исполняемые скрипты создаются как обычные файлы с расширением .groovy:

#!/usr/bin/env groovy
// script.groovy

println "Привет из Groovy скрипта!"
println "Аргументы: ${args.join(', ')}"

// Делаем файл исполняемым: chmod +x script.groovy
// Запуск: ./script.groovy arg1 arg2

Вопрос

Как получить аргументы командной строки в скрипте?


Ответ

Аргументы доступны через переменную args:

// script.groovy
if (args) {
    println "Получены аргументы:"
    args.eachWithIndex { arg, index ->
        println "${index}: $arg"
    }
} else {
    println "Аргументы не указаны"
}

Вопрос

Как использовать shebang в Groovy скриптах?


Ответ

Shebang позволяет запускать скрипты напрямую:

#!/usr/bin/env groovy
// Или конкретная версия
#!/usr/bin/groovy

println "Этот скрипт можно запускать как ./script.groovy"

Вопрос

Как импортировать классы в скриптах?


Ответ

Импорты работают так же, как в обычных классах:


import java.time.*
import groovy.json.JsonBuilder

def now = LocalDateTime.now()
def json = new JsonBuilder([time: now.toString()])
println json.toPrettyString()

Вопрос

Как использовать переменные окружения в скриптах?


Ответ

Переменные окружения доступны через System.getenv():

def home = System.getenv("HOME")
def user = System.getenv("USER")
def path = System.getenv("PATH")

println "Домашняя директория: $home"
println "Пользователь: $user"

// Безопасное получение с значением по умолчанию
def customVar = System.getenv("CUSTOM_VAR") ?: "значение по умолчанию"

Вопрос

Как выполнить внешнюю команду из скрипта?


Ответ

Внешние команды выполняются с помощью оператора execute():

// Простая команда
def result = "ls -la".execute().text
println result

// Команда с аргументами
def process = ["git", "status"].execute()
def output = process.text
def exitCode = process.exitValue()

// Обработка ошибок
def cmd = "nonexistent-command".execute()
cmd.waitFor()
if (cmd.exitValue() != 0) {
    println "Ошибка: ${cmd.err.text}"
}

Вопрос

Как передать входные данные во внешнюю команду?


Ответ

Входные данные передаются через поток ввода:

def process = "sort".execute()
process.withStreams { proc ->
    proc.out << "zebra\napple\nbanana\n"
    proc.out.close()
    println proc.text
}

Вопрос

Как использовать опции командной строки в скриптах?


Ответ

Опции обрабатываются с помощью класса CliBuilder:


import groovy.cli.commons.CliBuilder

def cli = new CliBuilder(usage: 'script.groovy [options]')
cli.h(longOpt: 'help', 'Показать помощь')
cli.v(longOpt: 'verbose', 'Подробный вывод')
cli.o(longOpt: 'output', args: 1, argName: 'file', 'Выходной файл')

def options = cli.parse(args)
if (!options) {
    return
}

if (options.h) {
    cli.usage()
    return
}

if (options.v) {
    println "Подробный режим включен"
}

if (options.o) {
    println "Выходной файл: ${options.o}"
}

Вопрос

Как завершить скрипт с кодом возврата?


Ответ

Код возврата устанавливается с помощью System.exit():

// Успешное завершение
println "Все хорошо"
System.exit(0)

// Ошибка
println "Произошла ошибка"
System.exit(1)

// Предупреждение
println "Предупреждение"
System.exit(2)

Вопрос

Как использовать логирование в скриптах?


Ответ

Логирование реализуется через java.util.logging или SLF4J:


import java.util.logging.Logger
import java.util.logging.Level

def logger = Logger.getLogger("MyScript")

logger.info("Информационное сообщение")
logger.warning("Предупреждение")
logger.severe("Критическая ошибка")

// Настройка уровня логирования
logger.level = Level.FINE

Вопрос

Как обрабатывать сигналы в скриптах?


Ответ

Сгналы обрабатываются через Runtime.addShutdownHook():

def shutdownHook = {
    println "Скрипт завершается..."
    // Очистка ресурсов
}

Runtime.runtime.addShutdownHook(new Thread(shutdownHook))

println "Скрипт запущен. Нажмите Ctrl+C для завершения."
while (true) {
    Thread.sleep(1000)
}

Вопрос

Как использовать конфигурационные файлы в скриптах?


Ответ

Конфигурационные файлы читаются и парсятся:

// config.properties
def props = new Properties()
new File("config.properties").withInputStream { props.load(it) }

def dbUrl = props.getProperty("database.url")
def dbUser = props.getProperty("database.user")

// config.groovy (Groovy конфигурация)
def config = new ConfigSlurper().parse(new File("config.groovy").text)
def apiUrl = config.api.url
def timeout = config.api.timeout

Вопрос

Как создать интерактивный скрипт?


Ответ

Интерактивные скрипты используют Система.console():

def console = Система.console()
if (console) {
    def username = console.readLine("Имя пользователя: ")
    def password = console.readPassword("Пароль: ")
    
    println "Привет, $username!"
} else {
    println "Консоль недоступна"
}

Вопрос

Как использовать цветной вывод в терминале?


Ответ

Цветной вывод реализуется через ANSI escape codes:

def RED = "\u001B[31m"
def GREEN = "\u001B[32m"
def YELLOW = "\u001B[33m"
def RESET = "\u001B[0m"

println "${GREEN}Успех!${RESET}"
println "${YELLOW}Предупреждение${RESET}"
println "${RED}Ошибка${RESET}"

Вопрос

Как обрабатывать прерывания (Ctrl+C) в скриптах?


Ответ

Прерывания обрабатываются через shutdown hooks:

def interrupted = false

def signalHandler = {
    println "\nПолучен сигнал прерывания..."
    interrupted = true
}

Runtime.runtime.addShutdownHook(new Thread(signalHandler))

// Основной цикл с проверкой прерывания
for (int i = 0; i < 100 && !interrupted; i++) {
    println "Шаг $i"
    Thread.sleep(1000)
}

if (interrupted) {
    println "Работа прервана"
} else {
    println "Работа завершена"
}

Вопрос

Как использовать многопоточность в скриптах?


Ответ

Многопоточность реализуется через Thread и ExecutorService:

// Простой поток
def thread = Thread.start {
    println "Фоновая задача"
    Thread.sleep(2000)
    println "Фоновая задача завершена"
}

println "Основной поток продолжает работу"
thread.join()

// Пул потоков
def executor = java.util.concurrent.Executors.newFixedThreadPool(2)
executor.submit({ println "Задача 1" } as Runnable)
executor.submit({ println "Задача 2" } as Runnable)
executor.shutdown()

Вопрос

Как работать с сетью в скриптах?


Ответ

Сетевые операции выполняются через java.net:

// HTTP запрос
def url = new URL("https://api.example.com/Данные")
def connection = url.openConnection()
connection.setRequestProperty("User-Agent", "Groovy Script")
def response = connection.inputStream.text
println response

// Проверка доступности хоста
def socket = new Socket()
socket.connect(new InetSocketAddress("google.com", 80), 5000)
socket.close()
println "Хост доступен"

Вопрос

Как использовать JSON в скриптах?


Ответ

JSON обрабатывается с помощью JsonSlurper и JsonBuilder:


import groovy.json.*

// Парсинг JSON
def jsonText = '{"name": "John", "age": 30}'
def parsed = new JsonSlurper().parseText(jsonText)
println parsed.name  // "John"

// Создание JSON
def builder = new JsonBuilder()
builder {
    name "John"
    age 30
    address {
        city "NYC"
    }
}
println builder.toPrettyString()

Вопрос

Как использовать XML в скриптах?


Ответ

XML обрабатывается с помощью XmlSlurper и MarkupBuilder:


import groovy.xml.*

// Парсинг XML
def xmlText = '<person><name>John</name><age>30</age></person>'
def parsed = new XmlSlurper().parseText(xmlText)
println parsed.name.text()  // "John"

// Создание XML
def writer = new StringWriter()
def builder = new MarkupBuilder(writer)
builder.person {
    name "John"
    age 30
    address(city: "NYC")
}
println writer.toString()

Вопрос

Как создать демон (daemon) на Groovy?


Ответ

Демоны создаются с бесконечным циклом и обработкой сигналов:

def running = true

// Обработка завершения
Runtime.runtime.addShutdownHook(new Thread({
    running = false
    println "Демон останавливается..."
}))

println "Демон запущен"

while (running) {
    // Основная логика демона
    println "Демон работает..."
    Thread.sleep(5000)
}

println "Демон остановлен"

Тестирование

Вопрос

Какие фреймворки для тестирования поддерживаются в Groovy?


Ответ

Groovy поддерживает несколько фреймворков для тестирования:

// JUnit (стандартный для Java)

import org.junit.Test
import static org.junit.Assert.*

// Spock (нативный для Groovy)

import spock.lang.Specification

// Geb (для веб-тестирования)

import geb.spock.GebSpec

// EasyB (поведенческое тестирование)

Вопрос

Как писать тесты с использованием Spock?


Ответ

Spock использует спецификации и блоки:


import spock.lang.Specification

class MathSpec extends Specification {
    def "сложение двух чисел"() {
        given:
        def calculator = new Calculator()
        
        when:
        def result = calculator.add(2, 3)
        
        then:
        result == 5
    }
    
    def "деление на ноль выбрасывает исключение"() {
        given:
        def calculator = new Calculator()
        
        when:
        calculator.divide(10, 0)
        
        then:
        thrown(ArithmeticException)
    }
}

Вопрос

Как использовать моки (mocks) в Spock?


Ответ

Моки создаются с помощью метода Mock():


import spock.lang.Specification

class ServiceSpec extends Specification {
    def "сервис вызывает зависимость"() {
        given:
        def dependency = Mock(Dependency)
        def service = new Service(dependency: dependency)
        
        when:
        service.process("Данные")
        
        then:
        1 * dependency.process("Данные") >> "processed"
    }
}

Вопрос

Как писать параметризованные тесты в Spock?


Ответ

Параметризованные тесты используют блок where:

def "умножение работает корректно"(int a, int b, int expected) {
    expect:
    a * b == expected
    
    where:
    a | b | expected
    2 | 3 | 6
    4 | 5 | 20
    0 | 10| 0
    -2| 3 | -6
}

Вопрос

Как использовать заглушки (stubs) в тестах?


Ответ

Заглушки создаются с помощью метода Stub():

def "заглушка возвращает фиксированное значение"() {
    given:
    def repository = Stub(Repository)
    repository.findById(_) >> new User(id: 1, name: "John")
    
    def service = new UserService(repository: repository)
    
    when:
    def user = service.findUser(1)
    
    then:
    user.name == "John"
}

Вопрос

Как писать интеграционные тесты в Groovy?


Ответ

Интеграционные тесты проверяют взаимодействие компонентов:

class DatabaseIntegrationSpec extends Specification {
    def dataSource
    
    def setup() {
        // Настройка тестовой базы данных
        dataSource = setupTestDatabase()
    }
    
    def "сохранение и получение пользователя"() {
        given:
        def user = new User(name: "John", email: "john@example.com")
        
        when:
        def id = userRepository.save(user)
        def loadedUser = userRepository.findById(id)
        
        then:
        loadedUser.name == "John"
        loadedUser.email == "john@example.com"
    }
}

Вопрос

Как использовать ассерты в Groovy?


Ответ

Groovy предоставляет мощные ассерты с подробной информацией:

def x = 5
def y = 10
def z = 15

// Стандартный assert
assert x + y == z

// Assert с сообщением
assert x > 10, "x должен быть больше 10"

// Множественные условия
assert x > 0 && y > 0 && z > 0

// Проверка коллекций
def list = [1, 2, 3, 4, 5]
assert list.size() == 5
assert 3 in list
assert list.containsAll([2, 4])

Вопрос

Как писать unit-тесты для классов Groovy?


Ответ

Unit-тесты проверяют отдельные методы:

class Calculator {
    def add(a, b) { a + b }
    def multiply(a, b) { a * b }
    def divide(a, b) {
        if (b == 0) throw new ArithmeticException("Division by zero")
        return a / b
    }
}

class CalculatorTest extends Specification {
    def calculator = new Calculator()
    
    def "addition works correctly"() {
        expect:
        calculator.add(2, 3) == 5
        calculator.add(-1, 1) == 0
    }
    
    def "division by zero throws exception"() {
        when:
        calculator.divide(10, 0)
        
        then:
        thrown(ArithmeticException)
    }
}

Вопрос

Как тестировать исключения в Groovy?


Ответ

Исключения тестируются разными способами:

// В Spock
def "method throws exception"() {
    when:
    riskyMethod()
    
    then:
    thrown(IllegalArgumentException)
    
    // Или с конкретным сообщением
    def exception = thrown(IllegalArgumentException)
    exception.message == "Invalid argument"
}

// В JUnit
@Test(expected = IllegalArgumentException.class)
public void testException() {
    riskyMethod();
}

// С проверкой сообщения
@Test
public void testExceptionMessage() {
    try {
        riskyMethod();
        fail("Expected exception");
    } catch (IllegalArgumentException e) {
        assertEquals("Invalid argument", e.getMessage());
    }
}

Вопрос

Как использовать PowerMock в Groovy?


Ответ

PowerMock позволяет мокать статические методы и конструкторы:


import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.api.mockito.PowerMockito
import org.junit.runner.RunWith

@RunWith(PowerMockRunner)
@PrepareForTest(Система.class)
class SystemMockTest extends Specification {
    def "mock System current time"() {
        given:
        PowerMockito.mockStatic(Система)
        PowerMockito.when(Система.currentTimeMillis()).thenReturn(1000L)
        
        when:
        def currentTime = Система.currentTimeMillis()
        
        then:
        currentTime == 1000L
    }
}

Вопрос

Как писать тесты для замыканий?


Ответ

Замыкания тестируются как обычные функции:

def "closure processes Данные correctly"() {
    given:
    def processor = { Данные -> data.toUpperCase() }
    
    when:
    def result = processor("hello")
    
    then:
    result == "HELLO"
}

def "closure with multiple parameters"() {
    given:
    def multiplier = { x, y -> x * y }
    
    expect:
    multiplier(3, 4) == 12
    multiplier(0, 5) == 0
}

Вопрос

Как тестировать метапрограммирование?


Ответ

Метапрограммирование тестируется через динамические вызовы:

def "dynamic method works correctly"() {
    given:
    class TestClass {
        String name
    }
    
    TestClass.metaClass.greet = { -> "Hello, ${delegate.name}!" }
    
    def obj = new TestClass(name: "John")
    
    when:
    def result = obj.greet()
    
    then:
    result == "Hello, John!"
}

Вопрос

Как использовать временные файлы в тестах?


Ответ

Временные файлы создаются и автоматически удаляются:

def "file processing works correctly"() {
    given:
    def tempFile = File.createTempFile("test", ".txt")
    tempFile.deleteOnExit()
    tempFile.text = "test content"
    
    when:
    def processor = new FileProcessor()
    def result = processor.process(tempFile)
    
    then:
    result == "PROCESSED: test content"
}

Вопрос

Как тестировать многопоточные приложения?


Ответ

Многопоточность тестируется с синхронизацией:

def "concurrent access is thread-safe"() {
    given:
    def counter = new AtomicInteger(0)
    def executor = Executors.newFixedThreadPool(10)
    def futures = []
    
    when:
    100.times {
        futures << executor.submit({
            counter.incrementAndGet()
        } as Callable)
    }
    
    def results = futures.collect { it.get() }
    executor.shutdown()
    
    then:
    counter.get() == 100
    results == (1..100).toList()
}

Вопрос

Как использовать @Shared аннотацию в Spock?


Ответ

@Shared используется для общих ресурсов между тестами:

class DatabaseSpec extends Specification {
    @Shared
    def database = setupExpensiveDatabase()
    
    @Shared
    def testData = loadTestData()
    
    def setup() {
        // Очистка перед каждым тестом
        database.clear()
    }
    
    def "test with shared database"() {
        given:
        database.insert(testData)
        
        when:
        def result = database.query("SELECT * FROM users")
        
        then:
        result.size() == testData.size()
    }
}

Вопрос

Как писать тесты для веб-приложений?


Ответ

Веб-тесты используют Geb или RestAssured:

// Geb для UI тестов

import geb.spock.GebSpec

class LoginSpec extends GebSpec {
    def "user can login successfully"() {
        given:
        to LoginPage
        
        when:
        username = "john"
        password = "secret"
        loginButton.click()
        
        then:
        at DashboardPage
        welcomeMessage.text() == "Welcome, John!"
    }
}

// RestAssured для API тестов

import io.restassured.RestAssured

def "api returns correct user Данные"() {
    given:
    def response = RestAssured.get("/api/users/1")
    
    expect:
    response.statusCode == 200
    response.jsonPath().getString("name") == "John"
}

Вопрос

Как использовать @Timeout в тестах?


Ответ

@Timeout ограничивает время выполнения теста:


import org.spockframework.runtime.model.Timeout

@Timeout(5)  // 5 секунд
def "long running test completes in time"() {
    given:
    def service = new SlowService()
    
    when:
    def result = service.processLargeDataset()
    
    then:
    result.size() > 0
}

Вопрос

Как тестировать обработку ошибок?


Ответ

Обработка ошибок тестируется через моки и исключения:

def "service handles repository errors gracefully"() {
    given:
    def repository = Mock(Repository)
    repository.findById(_) >> { throw new DatabaseException("Connection failed") }
    
    def service = new UserService(repository: repository)
    
    when:
    def result = service.findUserSafe(1)
    
    then:
    result == null
    // Или проверка логирования ошибки
}

Вопрос

Как использовать данные из внешних файлов в тестах?


Ответ

Данные загружаются из файлов:

def "process various input files"() {
    given:
    def testDataDir = new File("src/test/resources/test-Data")
    
    and:
    def inputFile = new File(testDataDir, fileName)
    def expectedOutput = new File(testDataDir, expectedFileName)
    
    when:
    def processor = new DataProcessor()
    def actualOutput = processor.process(inputFile)
    
    then:
    actualOutput == expectedOutput.text
    
    where:
    fileName          | expectedFileName
    "input1.txt"      | "output1.txt"
    "input2.json"     | "output2.json"
    "input3.xml"      | "output3.xml"
}

Вопрос

Как писать тесты для скриптов Groovy?


Ответ

Скрипты тестируются через выполнение и проверку вывода:

def "script processes arguments correctly"() {
    given:
    def scriptFile = new File("scripts/processor.groovy")
    
    when:
    def process = ["groovy", scriptFile.absolutePath, "arg1", "arg2"].execute()
    process.waitFor()
    def output = process.text
    def exitCode = process.exitValue()
    
    then:
    exitCode == 0
    output.contains("arg1")
    output.contains("arg2")
}

Практические примеры и шаблоны

Вопрос

Как реализовать паттерн Singleton в Groovy?


Ответ

Singleton реализуется через статический экземпляр:

class DatabaseConnection {
    private static DatabaseConnection instance
    
    private DatabaseConnection() {
        // Приватный конструктор
    }
    
    static DatabaseConnection getInstance() {
        if (!instance) {
            instance = new DatabaseConnection()
        }
        return instance
    }
}

// Использование
def connection = DatabaseConnection.getInstance()

Вопрос

Как реализовать паттерн Builder в Groovy?


Ответ

Builder реализуется через fluent interface:

class Person {
    String name
    int age
    String city
    
    static class Builder {
        private String name
        private int age
        private String city
        
        Builder name(String name) {
            this.name = name
            return this
        }
        
        Builder age(int age) {
            this.age = age
            return this
        }
        
        Builder city(String city) {
            this.city = city
            return this
        }
        
        Person build() {
            return new Person(name: name, age: age, city: city)
        }
    }
}

// Использование
def person = new Person.Builder()
    .name("John")
    .age(30)
    .city("NYC")
    .build()

Вопрос

Как реализовать паттерн Observer в Groovy?


Ответ

Observer реализуется через интерфейсы и списки наблюдателей:

interface Observer {
    void update(String message)
}

class Subject {
    private List<Observer> observers = []
    
    void addObserver(Observer observer) {
        observers << observer
    }
    
    void removeObserver(Observer observer) {
        observers.remove(observer)
    }
    
    void notifyObservers(String message) {
        observers.each { it.update(message) }
    }
}

class ConcreteObserver implements Observer {
    void update(String message) {
        println "Получено сообщение: $message"
    }
}

Вопрос

Как реализовать паттерн Strategy в Groovy?


Ответ

Strategy реализуется через интерфейсы и замены алгоритмов:

interface SortingStrategy {
    List sort(List items)
}

class QuickSortStrategy implements SortingStrategy {
    List sort(List items) {
        return items.sort { a, b -> a <=> b }
    }
}

class BubbleSortStrategy implements SortingStrategy {
    List sort(List items) {
        // Реализация пузырьковой сортировки
        def result = items.clone()
        for (int i = 0; i < result.size() - 1; i++) {
            for (int j = 0; j < result.size() - i - 1; j++) {
                if (result[j] > result[j + 1]) {
                    def temp = result[j]
                    result[j] = result[j + 1]
                    result[j + 1] = temp
                }
            }
        }
        return result
    }
}

class Sorter {
    SortingStrategy strategy
    
    void setStrategy(SortingStrategy strategy) {
        this.strategy = strategy
    }
    
    List sort(List items) {
        return strategy.sort(items)
    }
}

Вопрос

Как реализовать паттерн Factory Method в Groovy?


Ответ

Factory Method создает объекты через подклассы:

abstract class Document {
    abstract void open()
    abstract void save()
}

class TextDocument extends Document {
    void open() { println "Открываем текстовый документ" }
    void save() { println "Сохраняем текстовый документ" }
}

class PdfDocument extends Document {
    void open() { println "Открываем PDF документ" }
    void save() { println "Сохраняем PDF документ" }
}

abstract class DocumentFactory {
    abstract Document createDocument()
    
    Document createAndOpen() {
        def doc = createDocument()
        doc.open()
        return doc
    }
}

class TextDocumentFactory extends DocumentFactory {
    Document createDocument() {
        return new TextDocument()
    }
}

class PdfDocumentFactory extends DocumentFactory {
    Document createDocument() {
        return new PdfDocument()
    }
}

Вопрос

Как реализовать паттерн Decorator в Groovy?


Ответ

Decorator добавляет функциональность к объектам:

interface Coffee {
    double getCost()
    String getDescription()
}

class SimpleCoffee implements Coffee {
    double getCost() { 2.0 }
    String getDescription() { "Простой кофе" }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee
    
    CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee
    }
    
    double getCost() { decoratedCoffee.cost }
    String getDescription() { decoratedCoffee.description }
}

class MilkDecorator extends CoffeeDecorator {
    MilkDecorator(Coffee coffee) { super(coffee) }
    
    double getCost() { decoratedCoffee.cost + 0.5 }
    String getDescription() { "${decoratedCoffee.description}, с молоком" }
}

class SugarDecorator extends CoffeeDecorator {
    SugarDecorator(Coffee coffee) { super(coffee) }
    
    double getCost() { decoratedCoffee.cost + 0.2 }
    String getDescription() { "${decoratedCoffee.description}, с сахаром" }
}

Вопрос

Как реализовать паттерн Command в Groovy?


Ответ

Command инкапсулирует запросы как объекты:

interface Command {
    void execute()
    void undo()
}

class Light {
    void turnOn() { println "Свет включен" }
    void turnOff() { println "Свет выключен" }
}

class LightOnCommand implements Command {
    Light light
    
    LightOnCommand(Light light) { this.light = light }
    
    void execute() { light.turnOn() }
    void undo() { light.turnOff() }
}

class LightOffCommand implements Command {
    Light light
    
    LightOffCommand(Light light) { this.light = light }
    
    void execute() { light.turnOff() }
    void undo() { light.turnOn() }
}

class RemoteControl {
    Command command
    
    void setCommand(Command command) {
        this.command = command
    }
    
    void pressButton() {
        command.execute()
    }
    
    void pressUndo() {
        command.undo()
    }
}