Перейти к основному содержимому

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()
}
}