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.
- 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.
www.amaio.com