На информационном ресурсе применяются рекомендательные технологии (информационные технологии предоставления информации на основе сбора, систематизации и анализа сведений, относящихся к предпочтениям пользователей сети "Интернет", находящихся на территории Российской Федерации)

Подмешайте немного Groovy в приложения Java (исходники)

Если вы уже читали статьи из этой серии, вы могли увидеть, что существует множество интересных способов использования 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

class Person {
fname
lname
age
address
contactNumbers
String toString(){

numstr = new StringBuffer()
if (contactNumbers != null){
contactNumbers.each{
numstr.append(it)
numstr.append(" ")
}
}
"first name: " + fname + " last name: " + lname +
" age: " + age + " address: " + address +
" contact numbers: " + numstr.toString()
}
}
class Address {
street1
street2
city
state
zip
String toString(){
"street1: " + street1 + " street2: " + street2 +
" city: " + city + " state: " + state + " zip: " + zip
}
}
class ContactNumber {
type
number
String toString(){
"Type: " + type + " number: " + number
}
}
nums = [new ContactNumber(type:"cell", number:"555.555.9999"),
new ContactNumber(type:"office", number:"555.555.5598")]
addr = new Address(street1:"89 Main St.", street2:"Apt #2",
city:"Utopia", state:"VA", zip:"34254")
pers = new Person(fname:"Mollie", lname:"Smith", age:34,
address:addr, contactNumbers:nums)
println pers.toString()

В листинге 1 я объявил три класса -- Person, Address и ContactNumber. Приведенный ниже код создаёт объекты только что определенных типов и вызывает метод toString(). Пока всё довольно просто, но давайте посмотрим, что получилось в результате работы groovyc, приведенном в листинге 2:

Листинг 2. Классы, созданные командой groovyc

aglover@12d21 /cygdrive/c/dev/project/target/classes/com/vanward/groovy
$ ls -ls
total 15
4 -rwxrwxrwx+ 1 aglover user 3317 May 3 21:12 Address.class
3 -rwxrwxrwx+ 1 aglover user 3061 May 3 21:12 BusinessObjects.class
3 -rwxrwxrwx+ 1 aglover user 2815 May 3 21:12 ContactNumber.class
1 -rwxrwxrwx+ 1 aglover user 1003 May 3 21:12
Person$_toString_closure1.class
4 -rwxrwxrwx+ 1 aglover user 4055 May 3 21:12 Person.class

Ого, пять файлов .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

import org.farng.mp3.MP3File
import groovy.util.AntBuilder

class Song {

mp3file
Song(String mp3name){
mp3file = new MP3File(mp3name)
}
getTitle(){
mp3file.getID3v1Tag().getTitle()
}
getAlbum(){
mp3file.getID3v1Tag().getAlbum()
}
getArtist(){
mp3file.getID3v1Tag().getArtist()
}
String toString(){
"Artist: " + getArtist() + " Album: " +
getAlbum() + " Song: " + getTitle()
}
static getSongsForDirectory(sdir){
println "sdir is: " + sdir
ant = new AntBuilder()
scanner = ant.fileScanner {
fileset(dir:sdir) {
include(name:"**/*.mp3")
}
}
songs = []
for(f in scanner){
songs << new Song(f.getAbsolutePath())
}
return songs
}
}
songs = Song.getSongsForDirectory(args[0])
songs.each{
println it
}

Как вы можете видеть, сценарий очень прост, особенно учитывая его полезность в ситуации, подобной моей. Всё, что мне нужно делать, это передавать название определенной директории, и я получу нужную мне информацию (имя исполнителя, название песни и альбома) для каждого файла 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/
groovy-all-1.0-beta-10.jar;C:/dev/tools/groovy/ant-1.6.2.jar;
C:/dev/projects-2.0/jid3lib-0.5.jar
com.vanward.groovy.Songs c:\dev09\music\mp3s
Artist: U2 Album: Zooropa Song: Babyface
Artist: James Taylor Album: Greatest Hits Song: Carolina in My Mind
Artist: James Taylor Album: Greatest Hits Song: Fire and Rain
Artist: U2 Album: Zooropa Song: Lemon
Artist: James Taylor Album: Greatest Hits Song: Country Road
Artist: James Taylor Album: Greatest Hits Song: Don't Let Me
Be Lonely Tonight
Artist: U2 Album: Zooropa Song: Some Days Are Better Than Others
Artist: Paul Simon Album: Graceland Song: Under African Skies
Artist: Paul Simon Album: Graceland Song: Homeless
Artist: U2 Album: Zooropa Song: Dirty Day
Artist: Paul Simon Album: Graceland Song: That Was Your Mother
Рекомендуем
Популярное
наверх