Groovy Samuli Haverinen, Aki Hänninen 19. marraskuuta 2015 Groovy on oliokieli Java-alustalle (käännetään/tulkataan Java-tavukoodiksi), jonka syntaksi on hyvin Java-ystävällinen ja jota voidaan käyttää myös skriptikielenä. Groovya on suunniteltu käytettäväksi yhdessä Javan kanssa, ei korvaamaan sitä. 1 Historiaa Groovyn ensimmäinen versio ilmestyi vuonna 2003 ja alkuperäinen kehittäjä on James Strachan. Idean Groovyyn Strachan sai tutustuessaan Python ohjelmointikieleen. Kokeneena Java ohjelmoijana hän huomasi Pythonin sisältävän monia mielenkiintoisia ja hyödyllisiä Javasta puuttuvia ominaisuuksia. Tästä syntyi ajatus Javan kaltaisesta kielestä, joka toisi dynaamisten ohjelmointikielten edut Java-alustalle. [1] Groovyn uusin versio 2.4.4 julkaistiin 9.6.2015. Nykyään Groovyn kehityksestä on vastuussa Apache Software Foundationin alainen projektinhallintakomitea. [2] 2 Syntaksi Groovy on syntaksiltaan hyvin lähellä Javaa ja suurin osa Java koodista onkin syntaktisesti oikeaa Groovy koodia (semantiikassa saattaa tosin olla eroja). Javasta poikkeava syntaksi on yleensä helppoa ymmärtää, koska poikkeavuudet ovat lähinnä oikoteitä. Jos Javalla kirjoitetaan import java. u t i l. ; Date today = new Date ( ) ; niin Groovy skriptissä voidaan kirjoittaa today = new Date ( ) // Java // Java // Groovy 1
Groovyssa puolipisteet voidaan jättää pois, jos rivillä on vain yksi lauseke. Sulkeita voidaan jättää joissain tapauksissa pois. Groovyssa on oletuksena käytössä (import) osa Javan kirjastoista. Groovy skriptissä muuttujille ei tarvitse määritellä tyyppiä (joskin semantiikka on silloin hieman eri; kyseessä on dynaamisesti tyypitetty muuttuja). 3 Skriptit Tärkeimpiä motivaatioita Groovylle on ollut kehittää skriptikieli Java-alustalle. Groovy muistuttaa paljon Javaa, mutta se ei vaadi määrittelemään luokkia, jotta mitään voisi tehdä. Groovylla voidaan esimerkiksi tulostaa kansion sisältö suorittamalla komentoriviltä yhden rivin skripti new F i l e (. ). eachfilerecurse { p r i n t l n i t Javassa vastaavaa operaatiota varten pitäisi kirjoittaa luokka, jossa on main metodi, tallentaa koodi tiedostoon, kääntää se ja ajaa se. Tietysti Groovynkin tapauksessa kääntäminen Java-tavukoodiksi tapahtuu, mutta se on piilotettu kulissien taakse ja mitään tiedostoja tästä ei tallenneta. 4 Kääntäminen Groovya voidaan myös kääntää Java-tavukoodiksi. Ajoajan näkökulmasta Groovy on silloin vain tapa tuottaa tavallisia Java-luokkia. Kaikki Groovyn luokat ovat java.lang.object alaluokkia. Groovyn luokkia voidaan kutsua Javalla, periyttää Java-luokista ja sama toimii päinvastoin. Oletetaan esimerkiksi, että meillä on Groovylla kirjoitettu luokka MyGroovyClass. Tämä käännetään Java tavukoodiksi samaan tapaan kuin Javaluokka ja sitä voidaan kutsua Java-koodista tavalliseen tapaan new MyGroovyClass ( ) ; // c r e a t e from Java Näin Groovy-koodia on mahdollista helposti yhdistää osaksi Javalla kirjoitettua ohjelmaa (ja tietysti päinvastoin). 5 Kielen ominaisuuksia 5.1 Tyypit ja tyypitys Groovyssa kaikki tyypit ovat olioita ja käyttävät viitesemantiikkaa [1]. Javassa tämä päteee muuten, mutta perustietotyypit käyttävät arvosemantiikkaa. Javassa kuitenkin perustietotyypeistä on olemassa myös olioversiot. Esi- 2
merkiksi perustietotyypin int sijaan voidaan käyttää oliotyyppiä Integer. Groovyssa int vastaa Javan Integer tyyppiä. Koska Groovy on tarkoitettu käytettäväksi yhdessä Javan kanssa, niin Groovy hoitaa automaattisesti tyyppimuunnokset Javan perustietotyyppeihin ja takaisin. Jos esimerkiksi Groovysta kutsutaan Javan funktiota, joka ottaa parametrinä int tyypin, niin muunnos Groovyn int tyypista Javan perustietotyypiksi tapahtuu automaattisesti. Tyypitykseltään Groovy on valinnainen. Groovy skripteissä muuttujat ovat dynaamisia, jos tyyppiä ei erikseen määritellä. Luokkien sisällä on käytettävä def avainsanaa, jos halutaan määritellä dynaamisesti tyypitetty muuttuja. Jos tyyppi eksplisiittisesti määritellään, niin kyseessä on Javan tapaan staattisesti tyypitetty muuttuja, jolloin kyseisen viitteen päähän ei voi sijoittaa eri tyyppistä oliota. 5.2 Parametrinvälitys funktiokutsuissa Parametrien välittäminen funktiolle toimii Groovyssa samalla tavalla kuin Javassa, eli arvonvälityksenä (pass by value). Jos funktio saa parametrina perustietotyypin, tehdään tästä kopio funktion käytettäväksi. Jos kopion tilaa muutetaan, eivät muutokset näy funktion ulkopuolelle. Jos funktiolle vastaavasti välitetään jokin olio, tehdään olioon osoittavasta viitteestä kopio. Tällöin kyseisen olion tilaa on mahdollista sen metodeja kutsumalla muuttaa funktion sisällä niin, että muutokset näkyvät myös funktion ulkopuolella. 5.3 Funktionaalinen paradigma Groovyssa Funktiot ovat Groovyssa "ensiluokan kansalaisia"(first-class citizen). Groovyssa voidaan kirjoittaa sulkeumia (closure), joita voidaan sijoittaa muuttujiin sekä antaa funktioille parametrina. Sulkeumia voidaan myös käyttää iteroinnissa, loppumattomien virtojen (infinite streams) kanssa, map / filter / reduce -toiminnallisuuden toteuttamiseen, osana korkeamman asteen funktioita sekä moneen muuhun tarkoitukseen. [5]36 Muita funktionaalista paradigmaa toteuttavia ominaisuuksia on esimerkiksi mahdollisuus käyttää currying-tekniikkaa sekä koostaa sulkeumia toisista sulkeumista. Luokkia ja Collection-rajapinnan toteuttavia säiliöitä on mahdollista määritellä muuttumattomiksi (immutable). [6] 5.4 Groovy truth Groovyssa totuusarvo voidaan evaluoida muillekin tyypeille kuin booleanille, esimerkiksi lukuarvot ovat tosia, jos ne ovat erisuuria kuin nolla. Myös säiliöt 3
(esim. List ja Map) sekä merkkijonot evaluoituvat tosiksi, jos ne eivät ole tyhjiä. Tosia ovat myös olio-viitteet joiden arvo ei ole null sekä iteraattorit ja enumeraatiot, jotka eivät osoita viimeiseen alkioon. Lisäksi luokille voidaan kirjoittaa oma asboolean-funktionsa, joka palauttaa luokalle määritellyn totuusarvon. class Color { S t r i n g name boolean asboolean (){ name == green? true : f a l s e a s s e r t new Color (name : green ) a s s e r t!new Color (name : red ) 5.5 Kontrollirakenteet Groovyssa on käytössä for- ja while-silmukat sekä ehtorakenteista if-else - lauseet sekä switch-case -rakenne. Groovy tukee myös poikkeusten käsittelyä ja samanlaista try-catch-finally -rakennetta kuin Java. Kuten Javassakin (versiosta 7 alkaen) voidaan Groovyssa (versiosta 2.0 alkaen) ottaa kiinni useammanlainen poikkeus yhdessä koodilohkossa. try { /... / catch ( IOException NullPointerException e ) { / one b l o c k to handle 2 e x c e p t i o n s / If-else if-else -lauseet sekä for- ja while-silmukat toimivat samalla tavalla kuin Javassa. Groovyssa voidaan lisäksi käyttää säiliöiden, osavälien ja merkkijonojen läpikäymiseen rakennetta: // i t e r a t e over the c h a r a c t e r s in a s t r i n g def t e x t = " abc " def l i s t = [ ] for ( c in t e x t ) { l i s t. add ( c ) a s s e r t l i s t == [ " a ", " b ", " c " ] 4
Switch-rakenne on Groovyssa hieman erilainen kuin Javassa, esimerkiksi default-tapauksen täytyy Groovyssa olla listan viimeisenä. Sen sijaan Groovyssa switchillä voidaan tarkastella huomattavasti laajempaa joukkoa asioita kuin Javassa,kuten sitä onko annettu parametri jonkun tietyn luokan tyyppinen, sisältääkö jokin säiliö sen, kuuluuko arvo johonkin osaväliin (range) tai täsmääkö annettu säännöllinen lauseke arvon tostring-funktion paluuarvoon. def x = 1.23 def r e s u l t = " " switch ( x ) { case " foo " : r e s u l t = " found foo " // l e t s f a l l through case " bar " : r e s u l t += " bar " case [ 4, 5, 6, i n L i s t ] : r e s u l t = " l i s t " case 1 2.. 3 0 : r e s u l t = " range " case I n t e g e r : r e s u l t = " i n t e g e r " case Number : r e s u l t = " number " case ~/ f o / : // t o S t r i n g ( ) r e p r e s e n t a t i o n of x matches the p a t t e r n? r e s u l t = " foo regex " case { i t < 0 : // or { x < 0 r e s u l t = " n e g a t i v e " default : r e s u l t = " d e f a u l t " a s s e r t r e s u l t == " number " 5
5.6 Merkkijonot Groovyssa on kahdenlaisia merkkijonoja, normaaleja Javan merkkijonoja sekä Groovyn omia GString-merkkijonoja. Groovy tulkitsee heittomerkkeihin kirjoitetut merkkijonot sekä lainausmerkkeihin kirjoitetut merkkijonot jotka eivät sisällä interpoloituja GStringeihin voidaan interpoloida tekstiä muuttujista, esimerkiksi perinteinen tervehdyskoodi voisi näyttää seuraavalta def etunimi = P e r t t i d e f sukunimi = " Keinonen " def t e r v e = " Hyvää päivää, ${ etunimi [ 0 ]. $sukunimi " a s s e r t t e r v e. t o S t r i n g ( ) == Hyvää päivää, P. Keinonen 5.7 Muita ominaisuuksia Jo esiteltyjen ominaisuuksien lisäksi Groovy ainakin mainostaa itseään ajoja käännösaikaisella metaohjelmoinnilla, sekä Domain-Spesific kielten (DSL) toteuttamisella [7]. Näihin ei tässä tarkemmin puututa. 6
Viitteet [1] Koening D., Guillaume L., King P., Skeet J, 2011. Groovy in Action. 2nd ed. Manning Publications [2] Groovy Wikipedia article <URL:https://en.wikipedia.org/wiki/Groovy_(programming_language)> 19.11.2015 [3] Groovy Language Documentation <URL:http://docs.groovylang.org/latest/html/documentation/> 14.11.2015 [4] Groovy with Eclipse - Tutorial <URL:http://www.vogella.com/tutorials- /Groovy/article.html> 14.11.2015 [5] Functional Groovy presentation <URL:http://glaforge.appspot.com/article/functional-groovy-presentation> 18.11.2015 [6] <URL:http://docs.groovy-lang.org/latest/html/gapi/groovy/transform- /Immutable.html> 18.11.2015 [7] <URL:http://www.groovy-lang.org/> 19.11.2015 7