Groovy на практике: Взгляд на Groovy как на DSL для Java-разработчиков

Groovy: меньше кода – больше дела!

Эксперт в области Groovy Скотт Дэвис возобновляет серию Groovy на практике", последний выпуск которой относится к 2006 г. В этой статье описывается новейшая история языка, а также текущее состояние сообщества Groovy. Вы увидите, насколько легко можно начать разрабатывать приложения на Groovy.

Скотт Дэвис, главный редактор, AboutGroovy.com

Скотт Дэвис (Scott Davis) является международно признанным автором, лектором и разработчиком программного обеспечения. Среди его книг: Groovy Recipes: Greasing the Wheels of Java, GIS for Web Developers: Adding Where to Your Application, The Google Maps API и JBoss At Work.



28.05.2010

Эндрю Гловер (Andrew Glover) начал писать о Groovy для сайта developerWorks еще в 2004 г. Первой в свет вышла статья Почувствуйте Groovy в серии alt.lang.jre, которая была продолжена длинной чередой статей под общим названием Groovy на практике. В те времена еще не было выпущено ни одной книги по Groovy (сейчас их уже более десятка). Более того, до выхода Groovy 1.0 оставалось еще несколько лет. В общем, многое успело измениться с тех пор, как в 2006 г. на сайте developerWorks была опубликована последняя статья из серии "Groovy на практике".

В настоящее время количество загрузок дистрибутива Groovy в Интернет достигло примерно 35 тыс. копий в месяц. Приложения компаний, отличающихся своей консервативностью, например Mutual или Omaha, уже включают более 70 тыс. строк кода на Groovy. Список рассылки, относящийся к Groovy, является одним из наиболее популярных на сайте Codehaus.org, на котором и размещается проект Groovy (см. раздел Ресурсы). Groovy уступает по количеству загрузок и популярности списка рассылки только одному проекту: Grails – инфраструктуре для разработки Web-приложений, причем она написана на Groovy (см. раздел Ресурсы).

Выполнение кода, написанного на языках, отличных от Java, в JVM – это не просто распространенное явление, а центральная часть стратегии Sun. Groovy поддерживается Sun наряду с такими языками, как JavaScript, JavaFX, JRuby и Jython. То, что считалось чистыми экспериментами в 2004 г., сейчас стало нормой жизни.

Описание вопросов работы с Groovy в 2009 г. не сильно отличается от тех времен, когда Энди начал работать над первыми статьями серии. Синтаксис языка стабилизировался еще в 2005 г. Разумеется, в каждой версии появляются новые удобные возможности, однако они не нарушают обратной совместимости, которая ставится во главу угла руководителями проекта. Благодаря этому Groovy можно легко использовать в проектах Java, в которых желательно полагаться только на технологии, которые будут поддерживаться настолько долго, насколько будут эксплуатироваться приложения, созданные с их помощью.

Эта статья была написана с целью помочь опытным Java-разработчикам начать использовать Groovy. Несмотря на то, что кому-то может показаться, что статья ориентирована на начинающих, она посвящена исключительно вопросам практического применения Groovy. Таким образом, сразу после примера "Здравствуй, мир!" мы моментально перейдем к использованию Groovy в реальных задачах.

Об этой серии

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

При добавлении любой новой технологии в ваш инструментарий разработчика встают два основных вопроса: в каких случаях стоит ее использовать, а в каких нет. Groovy – это чрезвычайно мощный язык, но только если его применять для решения подходящих задач. Именно поэтому в статьях серии Groovy на практике рассматриваются вопросы практического применения Groovy с целью помочь вам разобраться в том, когда и как следует задействовать этот язык.

Установка Groovy

Если вы ранее не использовали Groovy, то первое, что вам следует сделать – это установить его. Установка заключается в выполнении нескольких простых шагов. Они напоминают этапы установки самой платформы Java, а также таких знакомых приложений как, например, Ant и Tomcat.

  1. Загрузите последнюю версию Groovy в виде архива ZIP или TAR.
  2. Распакуйте архив в любую директорию по вашему выбору (при этом следует избегать пробелов в именах каталогов).
  3. Создайте переменную окружения под именем GROOVY_HOME.
  4. Добавьте путь GROOVY_HOME/bin в переменную PATH.

Лучше всего Groovy выполняется на платформах Java 5 или 6. Чтобы проверить, какая версия Java у вас установлена, выполните команду java -version. Затем выполните команду groovy -version, чтобы убедиться, что Groovy был успешно установлен.

Подключаемые модули Groovy, предоставляющие такие возможности, как автозавершение или пошаговая отладка, существуют для всех основных сред разработки (IDE), в том числе Eclipse, IntelliJ и NetBeans. В отличие от Java, IDE не являются такой необходимостью для написания кода на Groovy. Благодаря лаконичному синтаксису языка, многие разработчики предпочитают пользоваться обыкновенным текстовым редактором. Groovy поддерживается как бесплатными редакторами, такими как vi и Emacs, так и недорогими коммерческими, например Textpad (для Windows®) и TextMate (для Mac OS X). За более подробной информацией обратитесь по ссылкам в разделе Ресурсы.

Далее вы убедитесь, что интеграция кода на Groovy в существующий проект Java не представляет трудностей. Все, что от вас требуется – это добавить JAR Groovy из каталога GROOVY_HOME/embeddable в classpath приложения, а также помесить Ant-задание javac внутрь задания groovyc (аналогичный механизм поддерживается Maven).

Однако не будем забегать вперед. Для начала рассмотрим обязательный пример – приложение "Здравствуй, мир!".


Здравствуй, мир Groovy

Ни для кого не секрет, для чего нужны примеры типа "Здравствуй, мир!". Они представляют собой простейшие приложения, которые могут быть написаны на данном языке программирования. В листинге 1 приведен пример приложения "Здравствуй, мир!" на Java. Самое интересное в нем – это то, сколько всего вам надо знать о языке, чтобы точно понять, как работает это приложение.

Листинг 1. Приложение "Здравствуй, мир!" на Java
public class HelloJavaWorld{
  public static void main(String[] args){
    System.out.println("Hello Java World");
  }
}

Вначале вам необходимо создать файл с именем HelloJavaWorld.java, содержащим строку public class HelloJavaWorld. Для многих начинающих Java-разработчиков открытием становится тот факт, что имя класса должно в точности совпадать с именем файла (с учетом регистра символов), иначе класс не скомпилируется. Кроме того, любознательные новички уже на этом этапе начинают интересоваться модификаторами доступа, такими как public и private.

Как правило, следующая строчка - public static void main(String[] args) – сразу вызывает шквал вопросов: Что такое static? Что такое void? Почему метод должен называться main? Что такое массив типа String? Наконец, попробуйте объяснить начинающему Java-программисту, что out – это public, static, final экземпляр типа PrintStream, являющийся членом класса System. Я никогда не забуду, как один студент после этого воскликнул: "Ничего себе! Я ведь всего-навсего хотел сказать "Hello".

Теперь, для сравнения, создадим такой же пример на Groovy. Создайте файл с именем HelloGroovyWorld.groovy и добавьте в него строчку из листинга 2.

Листинг 2. Приложение "Здравствуй, мир!" на Groovy
println "Hello Groovy World"

Да-да, этот код полностью эквивалентен примеру на Java, приведенному в листинге 1. В данном случае все детали реализации, которые непосредственным образом не относятся к решаемой задаче, оказываются скрытыми за одной строчкой кода, которая просто говорит "Hello". Выполните команду groovy HelloGroovyWorld, чтобы убедиться, что код работает как надо.

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


Подробнее о примере "Здравствуй, мир!"

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

Интерпретатор Groovy компилирует исходный код в памяти прежде, чем передать его JVM. Эту операцию можно провести вручную, выполнив команду groovyc HelloGroovyWorld.groovy. Однако если после этого вы попытаетесь выполнить полученный класс в JVM (с помощью команды java), то будет сгенерировано исключение (листинг 3).

Листинг 3. Исключение при попытке запуска скомпилированного класса Groovy, не включив JAR Groovy CLASSPATH
$ java HelloGroovyWorld
Exception in thread "main" java.lang.NoClassDefFoundError: groovy/lang/Script

Как было отмечено ранее, JAR-файл Groovy должен присутствовать в CLASSPATH. Выполните предыдущую попытку еще раз, добавив аргумент -classpath в java, как показано в листинге 4.

Листинг 4. Успешный запуск скомпилированного класса Groovy при помощи java
//Для UNIX, Linux и Mac OS X
$ java -classpath $GROOVY_HOME/embeddable/groovy-all-x.y.z.jar:. HelloGroovyWorld
Hello Groovy World

//Для Windows
$ java -classpath %GROOVY_HOME%/embeddable/groovy-all-x.y.z.jar;. HelloGroovyWorld
Hello Groovy World

Это уже лучше. Однако если вы хотите убедиться, что скрипт на Groovy действительно сохраняет исходную семантику кода на Java, то потребуется заглянуть внутрь байт-кода. Вначале выполните команду javap HelloJavaWorld, как показано в листинге 5.

Листинг 5. Анализ байткода Java
$ javap HelloJavaWorld
Compiled from "HelloJavaWorld.java"
public class HelloJavaWorld extends java.lang.Object{
  public HelloJavaWorld();
  public static void main(java.lang.String[]);
}

Как видите, в коде нет ничего удивительного, кроме разве что нескольких моментов, которые javac добавил за вас. В частности, необязательно было писать extends java.lang.Object или явно добавлять конструктор класса по умолчанию.

Далее выполните команду javap HelloGroovyWorld, как показано в листинге 6.

Листинг 6. Анализ байткода Groovy
$ javap HelloGroovyWorld
Compiled from "HelloGroovyWorld.groovy"
public class HelloGroovyWorld extends groovy.lang.Script{
  ...
  public static void main(java.lang.String[]);
  ...
}

Что такое DSL?

Заслуга в популяризации идеи предметно-ориентированных языков (domain-specific languages - DSL) принадлежит Мартину Фаулеру (Martin Fowler, см. раздел Ресурсы). Он определил DSL как "язык программирования, обладающий ограниченной выразительностью и предназначенный для использования в конкретной предметной области". Из "ограниченной выразительности" отнюдь не следует, что из языка нельзя извлечь серьезную выгоду. Это означает, что его конструкций достаточно только для того, чтобы применять его в "конкретной предметной области". Таким образом, DSL – это небольшие языки специального назначения в отличие от языков общего назначения, таких как Java.

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

Дэйв Томас (Dave Thomas) позже уточнил смысл термина DSL (см. раздел Ресурсы). По его словам: "В разговорах между собой эксперты предметной области ... используют жаргон – специализированный язык, который они изобрели, чтобы эффективнее и более кратко выражать мысли в своем кругу". Возможно, определение "Groovy – это краткий Java" точнее отражает связь между Groovy и Java. В следующем разделе мы приведем еще один пример того, как использование Groovy позволяет сокращать объем кода.

Как видно, компилятор groovyc самостоятельно создал класс с именем, соответствующим имени файла. При этом тот факт, что класс является наследником groovy.lang.Script, а не java.lang.Object, объясняет исключение NoClassDefFoundError при попытке выполнить данный код, не включив JAR Groovy в CLASSPATH. Среди остальных методов, добавленных компилятором, несложно обнаружить старый добрый метод public static void main(String[] args), в который был помещен код самого скрипта. Таким образом, семантика исходного примера на Java сохраняется, следовательно вы можете использовать весь ваш опыт и знания Java в процессе создания приложений на Groovy.

Далее рассмотрим пример передачи параметров командной строки в скрипт на Groovy. Создайте новый файл с именем Hello.groovy и добавьте в него содержимое листинга 7.

Листинг 7. Скрипт Groovy, принимающий параметры в виде аргументов командной строки
println "Hello, " + args[0]

Теперь выполните команду groovy Hello Jane из командной строки. Как и ожидалось, программа получила на вход строковый массив args. Возможно, использование args в данном примере покажется бессмысленным для начинающих, но не для опытных разработчиков на Java.

Groovy позволяет выделить самое необходимое из кода на Java. Скрипт Groovy, показанный выше, выглядит практически как исполняемый псевдокод. Он достаточно прост, чтобы быть понятным для новичков, но при этом позволяет сохранить всю мощь и выразительность Java, необходимую опытным программистам. Именно поэтому Groovy можно назвать DSL для платформы Java (см. заметку Что такое DSL?).


Простые объекты Groovy

Объекты Java Bean, или как их часто называют – POJO (простые Java-объекты), играют ключевую роль в программировании на Java. Класс POJO для представления объекта предметной области должен удовлетворять ряду требований. Класс должен быть помечен как public, а его поля – private, но при этом для них должны быть созданы открытые get- и set-методы. Пример типичного POJO в Java приведен в листинге 8.

Листинг 8. POJO-класс в Java
public class JavaPerson{
  private String firstName;
  private String lastName;

  public String getFirstName(){ return firstName; }
  public void setFirstName(String firstName){ this.firstName = firstName; }

  public String getLastName(){ return lastName; }
  public void setLastName(String lastName){ this.lastName = lastName; }
}

Простые объекты Groovy (Plain Old Groovy Objects – POGO) представляют собой замену POJO. Они сохраняют исходную семантику POJO, но при этом позволяют существенно сократить объем кода. В листинге 9 приведена краткая версия класса Person, написанная на Groovy.

Листинг 9. POGO-класс в Groovy
class GroovyPerson{
  String firstName
  String lastName
}

Все классы в Groovy по умолчанию имеют модификатор доступа public. Аналогично, все свойства являются private, а методы – public. Открытые get- и set-методы для каждого свойства автоматически добавляются компилятором. Скомпилируйте класс JavaPerson с помощью javac, а класс GroovyPerson с помощью groovyc. Далее проанализируйте байт-код обоих классов при помощи javap, чтобы убедиться, что класс Groovy выполняет ровно те же действия, что и Java, вплоть до наследования java.lang.Object. (В примере HelloGroovyWorld, приведенном выше, мы не указали вообще никакого класса, поэтому автоматически созданный класс был наследником groovy.lang.Script.)

Все это означает, что POGO можно использовать во всех местах, где используются POJO. Классы в Groovy представляют собой версии классов Java, в которых было оставлено только самое необходимое. После компиляции класса Groovy его могут использовать любые Java-классы точно так же, как если бы он был написан на Java. Чтобы убедиться в этом, создайте файл JavaTest.java и добавьте в него код, приведенный в листинге 10.

Листинг 10. Обращение к классам Groovy из Java
public class JavaTest{
  public static void main(String[] args){
    JavaPerson jp = new JavaPerson();
    jp.setFirstName("John");
    jp.setLastName("Doe");
    System.out.println("Hello " + jp.getFirstName());

    GroovyPerson gp = new GroovyPerson();
    gp.setFirstName("Jane");
    gp.setLastName("Smith");
    System.out.println("Hello " + gp.getFirstName());
  }
}

Несмотря на то что get- и set-методы явно не присутствуют в исходном коде на Groovy, этот тест наглядно демонстрирует, что они содержатся в скомпилированном классе. Однако этот тест был бы неполным без аналогичной проверки на Groovy. Создайте файл TestGroovy.groovy и добавьте в него код из листинга 11.

Листинг 11. Обращение к классам Java из Groovy
JavaPerson jp = new JavaPerson(firstName:"John", lastName:"Doe")
println "Greetings, " + jp.getFirstName() + ". 
   It is a pleasure to make your acquaintance."

GroovyPerson gp = new GroovyPerson(lastName:"Smith", firstName:"Jane")
println "Howdy, ${gp.firstName}. How the heck are you?"

Вероятно, первое, что бросилось в глаза – это синтаксис конструктора в Groovy, который позволяет именовать поля и, следовательно, перечислять их в произвольном порядке. Еще интереснее то, что этот подход работает как для классов Groovy, так и Java. Как это возможно? На самом деле Groovy сначала вызывает конструктор по умолчанию без аргументов, а затем устанавливает значения всех полей при помощи соответствующих методов доступа. Подобное поведение можно эмулировать в Java, но вам не удастся передавать значения полей в произвольном порядке, поскольку в Java нет именованных аргументов, а типом обоих полей является String.

Далее обратите внимание, что Groovy поддерживает традиционный механизм конкатенации строк, принятый в Java. Кроме того, в Groovy можно вставлять код в произвольное место строки при помощи конструкции ${}. Такие строки называются "строки Groovy" (Groovy Strings) или, для краткости, GString.

Наконец, при вызове get-методов класса можно увидеть еще один пример краткости синтаксиса Groovy. Вместо длинной записи gp.getFirstName() можно использовать gp.firstName для обращения к тому же свойству. Может показаться, что происходит прямое обращение к члену класса, однако на самом деле вызывается соответствующий get-метод. Set-методы работают аналогичным образом: gp.setLastName("Jones") эквивалентно gp.lastName = "Jones", поскольку второй вариант автоматически преобразуется к первому компилятором Groovy.

Надеюсь, что вы со мной согласитесь в том, что в каждом из примеров Groovy позволяет более кратко выразить эквивалентный код на Java. Поэтому его могут использовать "эксперты в предметной области" для более "эффективного общения в своем кругу коллег". В каком-то смысле это напоминает шутливую беседу между старыми приятелями.


В конце концов, Groovy – это тот же Java

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

Чтобы убедиться, что код на Groovy может выглядеть в точности как на Java, скопируйте файл JavaTest.java в JavaTestInGroovy.groovy и выполните команду groovy JavaTestInGroovy. Вы должны увидеть тот же вывод, что и ранее, однако заметьте, что в этом случае вам не требуется компилировать класс на Groovy перед запуском.

Таким образом, для перехода на Groovy от опытных Java-разработчиков не требуется практически никаких усилий. Поскольку любой код на Java также является корректным кодом на Groovy, данный переход осуществляется моментально. Вы так же можете продолжать использовать вашу версию Java, вашу IDE и работать в существующем рабочем окружении. Другими словами, ваша работа будет протекать фактически без изменений. Все, что требуется – это убедиться, что JAR Groovy находится в CLASSPATH проекта и немного изменить скрипт сборки, чтобы классы Groovy компилировались вместе с классами Java. В следующем разделе будет рассказано о том, как добавить задание groovyc в файл Ant-сборки build.xml.


Компиляции кода на Groovy c помощью Ant

Если бы javac поддерживал подключаемые модули (плагины), то можно было бы компилировать файлы Java и Groovy одновременно. Однако это не так, поэтому следует просто поместить Ant-задание javac внутрь задания groovyc. Таким образом, groovyc будет компилировать исходный код на Groovy, а javac – на Java, как и ранее.

Разумеется, groovyc может компилировать не только файлы Groovy, но и Java. Однако помните вспомогательные методы, которые groovyc добавлял в классы HelloGroovyWorld и GroovyPerson? Аналогичные методы будут также добавляться и в Java-классы, поэтому лучше, чтобы groovyc компилировал только код на Groovy, а кодом на Java занимался javac.

Для того чтобы вызывать groovyc из Ant, необходимо определить новое задание при помощи taskdef, а затем использовать задание groovyc так же, как и javac (см. раздел Ресурсы). Пример скрипта сборки Ant показан в листинге 12.

Листинг 12. Компиляции кода на Groovy и Java при помощи Ant
<taskdef name="groovyc"
         classname="org.codehaus.groovy.ant.Groovyc"
         classpathref="my.classpath"/>

<groovyc srcdir="${testSourceDirectory}" destdir="${testClassesDirectory}">
 <classpath>
   <pathelement path="${mainClassesDirectory}"/>
   <pathelement path="${testClassesDirectory}"/>
   <path refid="testPath"/>
 </classpath>
 <javac debug="on" />
</groovyc>

Кстати говоря, вам не кажется, что строки с конструкциями ${} внутри выглядят подозрительно похоже на GString? Будучи лучшим в своем классе языком, Groovy без зазрения совести перенял удачные решения, реализованные в других языках и библиотеках. Это далеко не последний раз, когда, глядя на некоторую конструкцию в Groovy, вы подумаете про себя: "Хм, где-то я уже это видел".


Заключение

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

Groovy является не единственным альтернативным языком для JVM. В частности, JRuby может быть отличным вариантом для разработчиков, знакомых с Ruby, а Jython – для тех, кто знает Python. Groovy, в свою очередь, идеально подходит для программистов, имеющих опыт работы с Java. Очень сильной стороной Groovy является компактный Java-подобный синтаксис, а также сохранение семантики Java. Кроме того, неплохо, что при переходе на новый язык не приходится заниматься вещами наподобие del *.* или rm -Rf *, не так ли?

В следующий раз мы поговорим об итерировании в Groovy. Зачастую приходится перебирать элементы коллекций один за другим, например содержимое списков, файлов или документов XML. Вы увидите в деле замыкание each, которое постоянно встречается в Groovy. До тех пор, я надеюсь, вы найдете множество возможных применений для Groovy.

Ресурсы

Научиться

Получить продукты и технологии

  • Groovy: загрузите последнюю версию Groovy в виде архива ZIP или TAR. (EN)

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Open source
ArticleID=493093
ArticleTitle=Groovy на практике: Взгляд на Groovy как на DSL для Java-разработчиков
publish-date=05282010