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

Groovy и Java — совместимость и отличия

Разработчику Архитектору

Groovy и Java — совместимость и отличия

Groovy компилируется в байт-код JVM и использует те же классы из JDK и Maven-зависимостей. На практике его часто описывают как «Java с синтаксическим сахаром», но это упрощение: есть отличия, которые ломают ожидания при переносе кода и при вызове из Java.


Что обычно работает без изменений

Большая часть валидного Java 8+ кода компилируется groovyc как Groovy:

  • объявления классов, интерфейсов, enum (с оговорками по новым фичам Java);
  • поля, методы, наследование, generics в исходниках;
  • вызовы JDK (java.util, java.time, NIO, JDBC и т.д.);
  • аннотации Java и многие библиотеки Spring, Hibernate, JUnit.
// Фрагмент «чистой» Java внутри .groovy-файла
import java.util.stream.Collectors

List<String> names = List.of("a", "b", "c")
List<String> upper = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList())

Обратное направление: Java-класс вызывается из Groovy напрямую, без обёрток.


Главные отличия (ловушки при переходе)

ТемаJavaGroovy
==Сравнение ссылок (для объектов)Вызов equals()
Идентичность== для примитивовis() или === (версия Groovy)
Деление int5 / 225 / 22.5
СтрокиТолько StringString и GString ("Hi $name")
Тип переменнойОбязателен (кроме var с Java 10+)def — вывод типа
Итерацияfor, Streamfor (x in list), each, collect
Проверка «пусто»Явные сравненияTruthiness: if (list)
ИсключенияПроверяемые throwsНе проверяются компилятором Groovy
СкриптНужен class + mainТоп-уровневые выражения в .groovy

GString и сравнение

def plain = 'Hello'
def gstr = "Hello"
assert plain == gstr // true — equals по содержимому
assert !plain.is(gstr) // false — разные классы

В Java == для двух new String("Hello") дало бы false (ссылки).

Операторы, которых нет в Java

  • ?. — безопасная навигация;
  • ?: — Elvis (значение по умолчанию для «ложного» операнда);
  • *.method() — spread по коллекции;
  • in / !in — принадлежность;
  • =~ / ==~ — регулярные выражения;
  • .. / ..< — диапазоны.

Ключевые слова только в Groovy

def, as, trait, in, it (неявный параметр замыкания), словесные and / or / not и др. — см. ключевые слова.


Вызов Groovy из Java

Работает предсказуемо, если Groovy-класс ведёт себя как обычный JavaBeans-класс:

  • явные типы в публичных методах;
  • без опоры на methodMissing / динамические свойства в API, видимом Java;
  • рантайм Groovy (groovy-*.jar) в classpath при необходимости.
// Java
Person person = new Person("Alice", 30);
System.out.println(person.getName());
// Groovy — компилируется в .class с getName()/setName()
class Person {
String name
int age
Person(String name, int age) {
this.name = name
this.age = age
}
}

Сложнее из Java:

  • скрипты без класса (нужен GroovyShell / GroovyClassLoader);
  • DSL с methodMissing, категории, глобальный metaClass;
  • методы только с def в сигнатуре на границе API.

Для публичного API библиотек предпочтительны явные типы и @CompileStatic на границе модулей.


Динамика vs статика в одном проекте

РежимКогдаПоведение
По умолчаниюСкрипты, Gradle, SpockMetaClass, позднее связывание
@TypeCheckedПоиск ошибок типовПроверка при компиляции, вызовы могут оставаться динамическими
@CompileStaticЯдро, hot pathВызовы как в Java, меньше рантайма Groovy

Типичная схема в enterprise: Java или @CompileStatic Groovy для домена, динамический Groovy для сборки, пайплайнов и тестовых спецификаций.


Что не стоит утверждать в документации и на собеседованиях

  • «Любой Java-код — валидный Groovy» — неверно для edge cases и новых конструкций только в Java.
  • «100% бинарная совместимость» — на практике совместимость высокая, но динамические фичи и различия операторов требуют внимания.
  • «Groovy заменил Java в вебе» — Grails уступил долю Spring Boot; Groovy остался в инфраструктуре.

Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).