Если вы уже читали статьи из этой серии, вы могли увидеть, что существует множество интересных способов использования Groovy, и одним из основных преимуществ Groovy является продуктивность работы программиста. Код Groovy чаще всего более прост и лёгок в написании, чем код Java, что делает его ещё более ценным дополнением к вашему инструментарию разработки.
С другой стороны, как я уже неоднократно отмечал в рамках серии, Groovy не является заменой языка Java и не предназначен для этого. Итак, вопрос состоит в том, можете ли вы внедрить Groovy в практику программирования Java, и полезно ли это, а если полезно - то когда ?В этом месяце я попытаюсь ответить на этот вопрос. Я начну с того, что вы уже знаете -- как сценарии Groovy компилируются в Java-совместимые файлы класса, после чего углублюсь в подробное описание того, как средства компиляции Groovy (groovyc) делают это возможным. Понимание того, как работает Groovy -- это первый шаг к использованию его в коде Java
Обратите внимание, что некоторые методики программирования, продемонстрированные в этой статье, лежат в основе инфраструктур Groovlets и GroovyTestCase Groovy, которые я рассматривал в прошлом месяце.
Правда ли, что браки заключаются на небесах?
В одной из предыдущих статей этой серии, когда я показывал, как проводить модульное тестирование обычных программ Java с помощью Groovy, вы могли заметить одну особенность: я компилировал сценарии Groovy. Действительно, я скомпилировал модульные тесты в обычные файлы .class Java и запустил их в процессе компоновки Maven.
Компиляция такого типа выполняется путем вызова команды groovyc, которая компилирует сценарии Groovy в старые добрые файлы .
class, совместимые с Java. Например, если в сценарии объявляется три класса, вызов groovyc приведёт к созданию трёх файлов .class. Сами файлы будут соответствовать стандартным правилам Java, согласно которым название файла .class совпадает с названием класса.Для примера давайте посмотрим на листинг 1, который создаёт простой сценарий, объявляющий несколько классов. Вы можете увидеть, что выводит команда groovyc:
Листинг 1. Декларация и компиляция класса в Groovy
package com.vanward.groovy |
В листинге 1 я объявил три класса -- Person, Address и ContactNumber. Приведенный ниже код создаёт объекты только что определенных типов и вызывает метод toString(). Пока всё довольно просто, но давайте посмотрим, что получилось в результате работы groovyc, приведенном в листинге 2:
Листинг 2. Классы, созданные командой groovyc
aglover@12d21 /cygdrive/c/dev/project/target/classes/com/vanward/groovy |
Ого, пять файлов .class! Существование файлов Person, Address и ContactNumber понятно, но зачем нужно ещё два?
Выясняется, что Person$_toString_closure1.class появился в результате наличия замыкания в методе toString() класса Person. Фактически он является внутренним классом Person. А откуда появился файл BusinessObjects.class?
Если внимательно посмотреть на листинг 1, можно заметить, что код, который я написал в основном теле сценария после объявления этих трех классов, стал файлом .class, имя которого я указал после названия сценария. В этом случае, именем сценария было BusinessObjects.groovy, поэтому код, не содержащий определения класса, был скомпилирован в файл .class с названием BusinessObjects.
Кодирование наоборот
Декомпиляция этих классов может быть очень интересной. Получившиеся файлы .java будут значительно больше по размеру, благодаря природе скрытого кода Groovy; однако, вы, вероятно, заметили разницу между классами, объявленными в сценарии Groovy (например, Person) и кодом вне классов (например, код BusinessObjects.class). Классы, определенные в Groovy, завершаются реализацией GroovyObject, а код, расположенный за пределами класса, входит в класс, расширяющий Script.
Например, если вы изучите файл .java, получившийся из BusinessObjects.class, вы увидите, что в нём определяется методы main() и run(). Очевидно, в методе run() содержится код, который я написал для создания новых экземпляров объектов, а метод main() вызывает метод run().
Суть этого вывода состоит в том, что чем лучше вы понимаете Groovy, тем проще будет встроить его в программы Java. "И зачем мне это нужно?" -- спросите вы? Итак, допустим, вы разработали что-то особенное в Groovy; правда, было бы неплохо встроить это также и в вашу программу на Java?
Просто ради наглядности, я сначала попытаюсь создать в Groovy что-либо полезное, после чего я рассмотрю различные способы его внедрения в обычную программу Java.
И снова музыка в Groovy
Я люблю музыку. В действительности моя коллекция компакт-дисков соревнуется по размерам с библиотекой книг по компьютерам. На протяжении многих лет я переписывал музыку на разные компьютеры и, по ходу дела, запутал свою коллекцию MP3 до предела - сегодня у меня множество каталогов, содержащих самую разнообразную музыку.
Недавно я предпринял первые шаги к наведению порядка в моей музыкальной коллекции. Я написал небольшой сценарий Groovy, который циклически проходил по коллекции файлов MP3, содержащихся в папке, и предоставлял мне подробную информацию о каждом файле -- исполнитель, название альбома и т.п. Этот сценарий показан в листинге 3:
Листинг 3. Очень полезный сценарий Groovy
package com.vanward.groovy |
Как вы можете видеть, сценарий очень прост, особенно учитывая его полезность в ситуации, подобной моей. Всё, что мне нужно делать, это передавать название определенной директории, и я получу нужную мне информацию (имя исполнителя, название песни и альбома) для каждого файла MP3 в этой директории.
Теперь давайте посмотрим, что мне нужно сделать, чтобы внедрить этот отличный сценарий в обычную программу Java, которая может организовывать музыку с помощью базы данных или даже проигрывать MP3.
Файлы классов и файлы классов
Как я уже говорил выше, первый вариант -- это просто компиляция сценария с помощью groovyc. В этом случае я ожидаю, что groovyc создаст как минимум два файла .class -- один для класса Song и другой для кода сценария, следующего после объявления Song.
На самом деле, groovyc создаст пять файлов .class. Это соотносится с тем фактом, что в Songs.groovy содержится три замыкания, два в методе getSongsForDirectory() и один в теле сценария, когда я формировал цикл по коллекции Song и вызывал println.
Поскольку три файла .class фактически являются внутренними классами Song.class и Songs.class, мне нужно сконцентрироваться только на двух файлах .class. Song.class накладывается напрямую на объявление Song в сценарии Groovy и реализует GroovyObject, тогда как Songs.class представляет код сценария, приведенный после того, как я определил Song, и после этого расширяет Script.
Теперь у меня есть два варианта внедрения только что скомпилированного кода Groovy в код Java: Я могу запускать код через метод main(), реализованный в файле класса Songs.class (поскольку он расширяет Script), также я могу включать Song.class в путь классов и использовать его так же, как и любые другие объекты в коде Java.
Будем проще
Вызов файла Songs.class с помощью команды java предельно прост, если вы не забываете включить зависимости Groovy и все возможные зависимости сценария Groovy. Самый простой способ включения необходимых классов Groovy состоит во включении в путь классов файлов jar "всё в одном", встраиваемых в Groovy. В моём случае, это файл groovy-all-1.0-beta-10.jar. Для запуска Songs.class мне также нужно не забыть включить используемую мной библиотеку MP3 (jid3lib-0.5.jar>), и поскольку я использую AntBuilder, мне также нужно включить в путь класса Ant. В листинге 4 всё сводится вместе:
Листинг 4. Groovy через командную строку Java
c:\dev\projects>java -cp ./target/classes/;c:/dev/tools/groovy/ |