Van két fontos különbség a Java és a Groovy között, amit mindenképpen megosztanék ezen a ponton. Groovyban nincsenek primitív típusok, még ha látszólag úgy is deklarálunk egy változót, az eredmény mindig egy példány lesz a memóriában. A másik, hogy a dupla egyenlőség vizsgálat, Javatól eltérően érték szerint hasonlít össze!
Minden Java fejlesztő rémálma a NullPointerException, éppen ezért Javaban a műveletek nagy részét megelőzi erre vonatkozó ellenőrzés, ami csak átláthatatlanabbá teszik a kódot. Groovyban ez a teljes vizsgálat elvégezhető egyetlen kérdőjel segítségével:
int foo = bar?.length ? bar.length : -1;Az eredmény, sokkal tömörebb kód, miközben az olvashatóságot sem rontja a szintaxis. A történet egyszerű. A kérdőjel helyére egy null ellenőrzést ékel a fordító.
Következő hasznos egyszerűsítés amit meg szeretnék említeni az un. Elvis operátort.
int foo = bar ?: -1;Az Elvis operátorral az alapértelmezett értéket lehet meghatározni, amennyiben az eredeti "érték" false vagy null.
Soron következő kedvencem a GString. Stringek összefűzésének problémájával a legtöbben már egészen biztos találkoztunk. Kis mennyiségű szöveg összefűzésénél még nem is akkora a probléma, mert az egy sorban elvégzett String összefűzés automatikusan egy StringBulder osztályra fordul. Nagyobb mennyiség esetén (hallottam olyan helyről, ahol a mai napig 80 karakter sorhossz) macerássá válik a művelet. A Groovy eszköztárában egy az Expression Language-re kísértetiesen hasonlító megoldást építettek.
String foo = "Foo" String bar = "${foo} Bar"Ezt a funkcionalitást kombinálva a több soros stringek deklarációjával, máris kézzelfogható előnyhöz jutunk:
def sql = """ select * from ${table} where bar = ${foo} """Fontos tudni, hogy a szimpla idézőjelek között létrehozott 'stringek' hagyományos java.lang.String példányok lesznek, a duplával pedig GStringek, ezért ha nem szeretnénk a GString sajátosságait kihasználni, mindig szimpla idézőjellel példányosítsuk stringjeinket.
Reguláris kifejezések használatát is lényegesen leegyszerűsítették a Groovys srácok.
Pattern pattern = ~/(.*)/ boolean find = 'foo' ==~ pattern Matcher m = 'foo' =~ pattern
Mint ahogy a bevezetőben említettem, Groovyban nincsenek primitív típusok, most lássuk, hogy ennek miért is van jelentősége. A fordító bizonyos operátorokat automatikusan átfordítja az objektum metódus hívásaira.
a + b // a.plus(b) a − b // a.minus(b) a ∗ b // a.multiply(b) a ∗∗ b // a.power(b) a / b // a.div(b) a % b // a.mod(b) a | b // a.or(b) a & b // a.and(b) a ^ b // a.xor(b) a++ o r ++a // a.next() a−− o r −−a // a.previous() a [ b ] // a.getAt(b) a [ b ] = c // a.putAt(b, c) a << b // a.leftShift(b) a >> b // a.rightShift(b) ~a // a.bitwiseNegate() −a // a.negative() +a // a.positive() a <=> b : a.compareTo(b)Ennek előnye egyrészt, hogy megkíméli a programozót rengeted felesleges gépeléstől, másrészt ezt a működést kihasználva saját osztályainkat is fel tudjuk készíteni, hogy értsék a különböző operátorokat. A Groovyban van is erre jó példa, pl. a Date osztályban.
def today = new Date() def tomorrow = today + 1 def yesterday = today - 1 assert today.plus(1) == tomorrow assert tomorrow.minus(1) == todayFontos megértenünk 2 dolgot az operátorok átfordítása kapcsán. Az egyik, hogy vannak esetek, amikor a visszatérési objektum tipusa más lesz, mint a operandusé.
StringBuilder sb = 'bar' << 'foo'A másik dolog, a túlcsordulást elhárító típusbővítés, ami azt jelenti például, hogy az 1 + 1.5 az ((BigInteger) 1.5).plus(1) -ra fordul, és az eredmény egy BigDecimal osztályban kerül tárolásra, hiába az Integer állt előbb. A Groovy decimális számok tárolására alapértelmezetten a BigDecimalt használja, elkerülendő a lebegőpontos számok ábrázolásából fakadó hibákat.
A következő érdekesség amire szeretném felhívni a figyelmet a Groovy osztálykezelése. A Groovy egy speciális osztályon keresztűl hozzáférést biztosít az osztályokhoz, és lehetőséget ad azok bővítésére.
String.metaClass.prefixFirstLette = { prefix -> return "${prefix}_${delegate.substring(0, 1)}" } println 'bar'.prefixFirstLette('foo');
Az előző példában egy újabb speciális Groovy osztállyal találkozhattunk, a Closure-val, mely osztály kiemelten fontos a nyelv szempontjából, és számtalan metódusnak átadható paraméterként.
Closure c = { i -> return i } println c.call(1)A Closure segítségével a Groovy szimulálni tudja a Javaból egyébként igencsak hiányzó névtelen függvények használatát.
Következő témakör, amelyet fontos kihangsúlyozni a Groovyval kapcsolatban, hogy natív támogatást nyújt listák és mapok kezelésére, ráadásul számos olyan funkcióval egészítették ki ezen osztályokat, amik megkönnyítik a velük végzett műveleteket. Pár példa a teljesség igénye nélkül:
def words = ['ant', 'buffalo', 'cat', 'dinosaur'] assert words.findAll{ w -> w.size() > 4 } == ['buffalo', 'dinosaur'] assert words.collect{ it[0] } == ['a', 'b', 'c', 'd'] def list = [[1,0], [0,1,2]].sort { item -> item.size() } assert list == [ [1,0], [0,1,2] ] assert [1, 3, 5] == ['a', 'few', 'words']*.size() //minden elemen végrehajtja a size() metódustA GDK plusz extraként kiegészít minden tömböt, kollekciót, és Stringet egy további toList() metódussal.
def greeting = 'Hello Groovy!' assert greeting[6..11] == 'Groovy' assert greeting[0,2,4] == 'Hlo'
A következő érdekesség az XML kezelés Groovyban. Okulva a Java hiányosságából, szintén natív támogatás van XML struktúrák kezelésére.
def builder = new groovy.xml.MarkupBuilder() builder.book { author 'Bar Foo' title 'sometitle' properties { pages 42 } } println builder
A Streamek kezelésében is hoz változást a Groovy. Javaval ellentétben nem kell ciklust írnunk a tartalom áttöltéséhez.Bar Foo sometitle 42
def address = 'http://jpattern.blogspot.com/favicon.ico' def file = new FileOutputStream(address.tokenize("/")[-1]) def out = new BufferedOutputStream(file) out << new URL(address).openStream() out.close()
Utoljára hagytam a legkevésbé fontos, de talán mégis hasznos újítást az importok területén. Lehetőség van Groovyban importált osztály-t aliasszal megjelelölni.
import org.springframework.context.i18n.LocaleContextHolder as LCH ... def locale = LCH.getLocale()
A pozitívumok után következzenek a negatívumok, bár személy szerint nem sok ilyet találtam. Az első, hogy a Groovy nem támogatja belső osztályok definiálását, ami szerintem a Java eszköztárának egy fontos kelléke. A Másik, hogy dinamikus típusú nyelv lévén az IDE támogatás meg sem közelíti a Javaét. Bár mindhárom elterjedt IDE (Netbeans, Eclipse, IntelliJ) rendelkezik Groovy támogatással, Javahoz szokott fejlesztőként számtalan kényelmi funkciót kell nélkülözni.
Véleményem szerint egy elég erőteljes nyelv lett a Groovy, a fejlesztők igyekeztek a Java hiányosságaiból tanulni, miközben megőrizték a Javaban rejlő erőt teljes mértékben. Bár mindenki azt csinál amit akar, én személy szerint alapos Java ismeretek nélkül nem ajánlom a nyelvet kezdőknek, ugyanis ahhoz elég sok dologban tér el a Javatól, hogy rossz szokásokat fejlesszen későbbi Java programozáshoz. Ilyen pl. a dupla egyenlőség vizsgálat, a String automatikus StringBuilderré alakítása bizonyos operátorok használatakor, A streamek kezelése, stb. Remélem további ismerkedésre inspirál mindenkit ez a kis írás, és sokan kiegészítik ezzel a remek eszközzel programozói repertoárjukat.