Aneb jak a proč Groovy
autor: Václav Pech
Nedávno jsem měl možnost seznámit se s
programovacím jazykem Groovy a musím říct, že mě
nadchl. V tomto článku bych se rád podělil o své
dojmy s ostatními vývojáři, pokusil se vysvětlit, co
zajímavého přináší programovací jazyk Groovy do světa
Javy a proč stojí zato se o Groovy něco dozvědět. Jde
o volné a nenáročné úvodní představení filozofie a
principů Groovy, včetně několika motivačních ukázek
kódu, bez snahy o absolutní úplnost.
Osoby vystupující v příběhu a jejich jména jsou
smyšlené, případná podobnost s žijícími i nežijícími
osobami je veskrze náhodná. Ukázky kódu použité v
příběhu jsou pravé a funkční.
Stalo se jednoho nedávného dne v
jedné softwarové společnosti (záznam z IM historie)
Franta: Čau, jak je?
Bohouš: Nazdar! Prima, a co Ty?
Franta: Pohoda. Máš chvilku?
Potřeboval bych trochu helpnout.
Bohouš: O co 'de? Včera jsme
releasovali, tak mám teď celkem volno.
Franta: Rozjíždím nový projekt a
hodil by se mi někdo, jako jsi ty. Nejdřív bys nám
mohl napsat v Groovy takový …
Bohouš: Aha?! No to bude asi
problém, o Groovy nic nevím.
Franta: To se rychle naučíš, je
to pohoda.
Bohouš: Přece nebudu na starý
kolena utíkat od Javy!
Franta: Jak utíkat? Groovy je
budoucnost Javy.
Bohouš: Loni to na mě zkoušel
Pepa, že prý Ruby je budoucnost.
Franta: To měl docela pravdu.
Bohouš: Houby pravdu, vůbec jsem
tomu jeho kódu nerozuměl, taková náhodná změť znaků.
S Javou to neintegruje, na JBossu to nepustíš, žádné
pořádné IDE to nemá, prostě všechno znova, jak když
jsem začínal s Javou někdy v minulým století.
Franta: Ruby se rychle vyvíjí,
teď už s Javou docela integruje a i ta IDEčka už
jsou. Poslechni si CZ podcast číslo 15.
Bohouš: OK, ale ty po mně chceš
Groovy.
Franta: Groovy je takové
"Ruby v Javě". Syntaxe Groovy je
nadmnožinou syntaxe Javy, tedy zatím s několika málo
výjimkami, ale to se brzy spraví. Takže můžeš dál
psát Java kód, akorát to ukládáš do .groovy souborů a
je z tebe Groovy junior!
Bohouš: Fajn, ale to asi nebude
stačit pro ten tvůj projekt, co?
Franta: Groovy syntaxi se můžeš
učit postupně. Pro Java vývojáře je to celkem snadná
cesta do světa dynamických jazyků.
Bohouš: A co integrace s
existujícím Java kódem?
Franta: Bez obav. Groovy kód
můžeš normálně míchat s Java kódem v jednom projektu,
z Groovy volat Javu, z Javy volat Groovy, extendovat
či implementovat napříč oběma jazyky. Samozřejmě máš
v Groovy k dispozici celé známe JDK, přilinkovat
můžeš Java knihovny, takže dál klidně používej log4j,
hibernate, spring, prostě co chceš a na co jsi
zvyklý. Knihovny se nemusí přepisovat pro Groovy. OK?
Bohouš: OK. A co JBoss?
Franta: Groovy kompiluje do Java
bytecode, klidně piš Groovy i pro EE aplikačky.
Například v Seam 2.0 můžeš teď psát komponenty v
Groovy.
Výhody syntaxe Groovy
Bohouš: Takže s Groovy můžu
krásně žít uvnitř Java světa, ale proč tedy jiná
syntaxe?! Já v Javě napíšu všechno. Nevidím důvod,
proč bych potřeboval nový jazyk. Abych ušetřil pár
řádků kódu? Dneska ti všechna IDEčka kompletují nebo
generují kód, ani nemají problém nepotřebné části
schovat.
Franta: Koukni na tohle:
company.employees.grep{it.age > 30}.name
Odhadneš, co ten kód dělá? Napovím ti, že kód ve
složených závorkách se nazývá closure a technicky je to
metoda předaná jako parametr metodě grep.
Identifikátor označený it zastupuje v kódu
closury její jediný parametr.
Bohouš: Vyjede všechny starouše
ve firmě, tedy jejich jména.
Franta: No, fajn. Ten kód jasně
říká, co dělá, ne jak to dělá. Takové traverzaci přes
několik objektů a kolekcí v Groovy se říká GPath. Jak
bys to napsal v Javě?
Bohouš: Jednoduše. Iteroval bych
přes ty employees, u každého bych vyhodnotil podmínku
na age a případně ho přidal do resultu.
Franta: Takže nějak takhle:
final List result=new ArrayList();
for (final Employee employee : company.getEmployees()) {
if (employee.getAge()>30) result.add(employee.getName());
}
Bohouš: No, konečně pěkný kus
kódu.
Franta: Jenže v Groovy by stačil
jeden řádek.
Bohouš: No co, 3x delší.
Franta: Statisticky to vychází
tak 3x – 4x delší v Javě než v Groovy.
Bohouš: Většinu toho kódu ti
stejně nageneroval editor.
Franta: To u toho Groovy kódu
taky.
Bohouš: Kdes na to vzal editor?
Franta: Normálně stáhni plugin
do Eclipse, IDEy nebo NetBeans a můžeš začít.
Bohouš: Počkej, ještě jsem nic
neslíbil.
Franta: Dobře, tak budu
pokračovat v masáži.
Masáž kódem
Bohouš: Skončili jsme u toho, že
editor mi stejně nageneruje většinu toho balastu
kolem, takže mě tolik netrápí, že je Groovy kód
kompaktnější 3x.
Franta: … až 4x.
Bohouš: Klidně 10x, to pro mě
není argument.
Franta: Ale měl by být. Kód
napíšeš jednou, ale potom ho stokrát znovu čteš,
hledáš v něm chyby a ladíš. Nebo třeba někdo jiný to
musí časem pochopit a z různých důvodů upravit. Kód
se píše pro lidi.
Bohouš: Oukej.
Franta: Koukni na tohle:
Jednoduše zapsaný cyklus
10.times{print 'Huh'}
Násobení stringů pomocí standardně přetížených
operátorů, vypíše aaabb
String text='a'*3+'b'*2
Třída String v Groovy umí iterovat přes všechny
znaky a na každý zavolat dodaný kód v podobě closure.
Vypíše daný text převedený do upper-case.
'''
multi-line text
which will be printed
in upper case
'''.each {print it.toUpperCase()}
Práce s kolekcemi, pro úplnost uvedu, že assert
vyhodnocuje pravdivost podmínek
["Joe", "Dave"].each {println it} //vypíše všechny položky pole
assert ["Joe", "Dave"][0] == "Joe" //první položka pole
assert ["Joe", "Dave", "Martin"][1..2] == ["Dave", "Martin"] //docela efektní způsob zápisu výběru části pole
Přidávání do seznamu
company.employees << new Employee(name:"Bohouš", age:28, status:"junior")
nebo
company.employees += new Employee(name:"Bohouš", age:28, status:"junior")
Bohouš: Hezký, ale už jsem
senior.
Franta: Tak sorry.
Bohouš: A co odebírat ze
seznamu, umíš?
Franta: Jo.
company.employees -= bohous
Bohouš: Proč do toho
konstruktoru předáváš parametry přes jména, např.
'age:28'? To je nutný?
Franta: Není, klidně to dělej
jako do teď v Javě, ale takhle máš možnost zadat jen
některé parametry a nemusiš kvůli tomu definovat
milióny konstruktorů s různými sadami parametrů, nebo
předávat null, když nechceš některý z parametrů
zadávat.
Teď ti ještě ukážu, jak jsou mapy a kolekce
podporovány přímo na úrovni syntaxe jazyka.
Map map=["joe":10, "dave":12]
Dva různé způsoby přístupu k položkám v mapě
assert map["dave"] == 12
assert map.dave == 12
Dva různé způsoby nastavení hodnot položek v mapě
map.dave = 20
assert map.'dave' == 20
map['dave'] = 30
assert map.'dave' == 30
Groovy přidává nové metody k třídám v JDK, včetně
přetížení operátorů, hlavně ke stringům, číslům,
kolekcím či polím.
Podívej třeba na datumy, tohle v Javě na jeden řádek
nenapíšeš:
use (TimeCategory) {
println "Tomorrow: ${1.day.from.today}"
println "A week ago: ${1.week.ago}"
println "Date: ${1.month.ago + 1.week + 2.hours - 5.minutes}"
}
Kolekce toho taky umí hodně navíc, třeba jejich
metody any a every stojí za povšimnutí.
if (company.employees.any {it.status == 'junior'}) offerTrainingTo(company)
if (company.employees.every {it.status == 'senior'}) hireForNextProject(company)
Tomuhle vnořování Groovy do Stringů se říká
GString, uvnitř těch složených závorek můžeš psát
libovolný Groovy kód.
String employeeDescription="Employee ${employee.name}, ${employee.age} years old"
Takový přehlednější substring, všimni si toho
negativního indexu na konci (vrátí string od nulté
pozice do páté pozice od konce).
//Vytáhne jméno souboru bez přípony
String fileNameWithoutExtension='tomcat_log_2007_07_10_02x_demo.log'[0..-5]
Regulární výrazy jsou zabudovány přímo do Groovy…
if ('abcabcabc' ==~ /(abc)+/) {
println "Je to tam"
}
… a můžem třeba hned kouknout, jestli se někde na
webu nepíše o Groovy nebo Ruby:
['http://www.javalobby.org', 'http://www.theserverside.com', 'http://www.infoq.com'].each {address ->
[/Groovy/, /Ruby/, /Grails/, /Rails/].each {pattern ->
if (new URL(address).text =~ pattern) {
println "O $pattern se píše na $address"
}
}
}
Bohouš: Prima
Buildery
Franta: Taky tě možná zaujme,
jak se v Groovy pracuje s XML. Třeba vytvoření nového
XML dokumentu pomocí Markup Builderu.
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.invoice() {
customer() {
name('Ferda')
address(type:'Delivery') {
street('Pod dubem')
number('10')
}
address(type:'Billing') {
street('Za dubem')
number('20')
}
}
product(id:'00001') {
quantity(50)
}
}
println writer.toString()
A jak bude vypadat výsledný XML dokument…?
Bohouš: No, myslím, že je to
zřejmé.
Franta: Buildery jsou užitečné
na práci se stromovými datovými strukturami, třeba v
podobě XML dat, HTML dokumentů, nebo třeba GUI
komponent. Díky tomu potom struktura kódu pracujícího
s hierarchickou datovou strukturou odpovídá vizuálně
i logicky té manipulované struktuře. To samozřejmě
vede k menšímu počtu chyb při psaní a snazšímu
pochopení a úpravám kódu později. Několik takových
builderů budeme potřebovat nadefinovat v tom našem
projektu pro naše vlastní data.
Closures
Bohouš: Ještě něco??
Franta: Třeba properties – getry
a setry pro property se ti vytvoří až za runtime,
pokud je nepotřebuješ upravit vlastním kódem.
Bohouš: To bude v Javě 7 asi
taky.
Franta: Je na čase. Taky by v ní
měly přibýt closures.
Bohouš: To už v Groovy je, že?
Franta: No jasně, s nimi se to
teprve pěkně rozjíždí.
def triple = {x -> x * 3} //definice closury ztrojnásobující svůj jediný parametr
{[1, 2, 3].collect {triple(it)} //vrátí nový seznam se všemi hodnotami ztrojnásobenými
Closury můžeš ukládat do proměnných nebo fieldů, a tak
změnou closury za běhu změnit chování objektů. Nebo
můžeš fixovat hodnotu některých parametrů closury
pomocí metody curry() a vytvořit tak novou closuru s
upraveným chováním.
def multiply = {x, y -> x * y} //obecné násobení dvou čísel
assert 6 == multiply(2, 3)
def triple = multiply.curry(3) //definice nové closury ztrojnásobující svůj jediný parametr, něco jako {3, y -> 3 * y}
[1, 2, 3].collect() {triple(it)} //vrátí nový seznam se všemi hodnotami ztrojnásobenými
Bohouš: Paráda.
Franta: To tedy jo.
Shrnutí
Bohouš: Takže z toho, co's
řekl, vyplývá, že Groovy:
- integruje do Javy, včetně EE,
- umožňuje využívat všechny existující Java
knihovny, včetně JDK samotného, - obaluje standardní třídy z JDK, zvláště čísla,
datumy, stringy a kolekce novou funkcionalitou, - rozšiřuje syntaxi Javy o podporu properties,
closures, named parametry a snadnou práci se
seznamy a mapami, - regulární výrazy jsou standardní součástí
jazyka, - GString dovoluje vkládat Groovy kód do stringů,
což umožňuje definovat vlastně takové jednoduché
šablony, - pro hierarchické datové struktury Groovy nabízí
koncept builderů, s před-připravenými buildery pro
XML, HTML, Swing a stromy, - nabízí GPath pro snadné traversování mezi
objeky, včetně kolekcí, - zahrnuje ranges (např. 2..5)
jako samostatný datový typ.
Franta: Přesně. Ještě jsme se
nepustili do několika pokročilejších věcí, třeba:
- vytváření custom builderů pro vlastní
hierarchická data, - integrovaná práce s textovými šablonami,
- groovlets pro rychlý vývoj webovských aplikací,
- práce s relačními databázemi,
- meta-programming – dynamické vyvolávání metod a
možnost definovat metodu či celou třídu za běhu, - přidávání metod k existujícím knihovním Java a
Groovy třídám, - vytváření interních DSL (Domain Specific
Language), - a ještě pár dalších věcí, na které jsem
zapomněl.
Pokud budeš mít chuť, můžeš mrknout na tyhle linky:
- Groovy
home - Kniha
Groovy in Action - Podařená prezentace Groovy pro začátečníky v
podání Jeffa Browna, vývojáře Groovy jazyka - Andrew Glover: Feeling Groovy – Groovy
tutoriálek - Eclipse Groovy integration
- IntelliJ IDEA Groovy integration
- Groovy plugin pro NetBeans
- Blog
Martina Adámka – jednoho z autorů Groovy
pluginu pro NetBeans
Franta: Tak co, je Groovy žúžo?
…
…
Poznámka: Groovy neboli
zaběhnutý, zaběhaný, rutinní, prima, bezva, fajn,
žúžo.