k47.cz  — každý den dokud se vám to nezačne líbit
foto Praha výběr povídky kultura
twitter FB RSS

Groovy

1. 8. 2011 — k47 (CC by-nc-sa) (♪)

Groovy je skrip­to­vací jazyk, který běží v JVM, vy­chází z Javy a dokáže vy­u­ží­vat všechny knihovny v ní na­psané. Oproti sta­ticky ty­po­vané Javě je ty­po­vaný dy­na­micky (pod­po­ruje tedy duck typing). Navíc do sa­mot­ného jazyka při­dává ně­ko­lik vy­lep­šení in­spi­ro­va­ných Py­tho­nem, Ruby nebo Small­tal­kem, které mohou zá­sadně zvýšit pro­duk­ti­vitu psaní kódu za cenu vyšší run­time režie. Výkon vý­sledné apli­kace je mnohem menší než ekvi­va­lent na­psaný v čisté Javě, což je způ­so­beno zmí­ně­ným dy­na­mic­kým ty­po­vá­ním, které vnitřně hojně vy­u­žívá re­flexi. Přesto může být po­u­žití Groovy vý­hodné. Když vez­meme v potaz kratší čas vývoje a mož­nost po­u­ží­vat všechny Ja­vov­ské knihovny, může se z Groovy stát „le­pi­dlo“, které rychle po­spo­juje ně­ko­lik kom­po­nent.


Uzá­věry

Nej­vět­ším ta­há­kem Groovy jsou uzá­věry neboli clo­su­res (těch se možná, ale opravdu jenom možná do­škáme v Javě 7). Clo­sure je něco jako ano­nymní funkce, která ovšem může při­stu­po­vat k pro­měn­ným de­fi­no­va­ným v kon­textu, jež ji ob­klo­puje. Nemusí tedy s okol­ním světem ko­mu­ni­ko­vat jenom přes pa­ra­me­try a ne­chová se tedy zcela jako ano­nymní funkce, ale spíš jako řídící struk­tura nebo blok kódu. Díky tomu se dá pomocí uzá­věrů snadno udělat to, co by se jenom velice těžko ob­chá­zelo bez nich (na­pří­klad po­u­ži­tím ano­nym­ních tříd zná­mých z Javy). Uzá­věry by nebyly ani zda­leka tak uži­tečné, kdyby nebyly k mnoha ob­jek­tům ze stan­dardní Ja­vov­ské knihovny (pře­de­vším ke ko­lek­cím) při­dány nové metody, které uzá­věry vy­u­ží­vají.

Všechno osvětlí malý pří­klad z Wi­ki­pe­die.

Ja­vov­ský kód:

for (String it : new String [] {"Rod", "Carlos", "Chris"}) {
  if (it.length() <= 4) {
     System.out.println(it);
  }
}

ekvi­va­lentní Groovy kód:

["Rod", "Carlos", "Chris"].findAll{ it.size() <= 4 }.each{ println it }

Metoda fin­dAll vrátí ko­lekci ele­mentů, které od­po­ví­dají pod­mínce z clo­sure. Když nejsou uve­deny pa­ra­me­try ex­pli­citně, po­u­žije se im­pli­citní pro­měnná it. Ex­pli­citní forma clo­sure by vy­pa­dala takto: { elem -> elem.size() <= 4 }. Navíc metoda (pokud není de­kla­ro­vána jako void) au­to­ma­ticky vrací vý­sle­dek po­sledně vy­hod­no­ce­ného výrazu. Proto si můžeme ušet­řit mnoho zápisů return a také proto se uve­dená clo­sure vy­hod­notí, jak bychom před­po­klá­dali. Metoda each pak ite­ruje nad vrá­ce­nou ko­lekcí. Díky tomu (a dalším no­vin­kám, které zmíním poz­ději), prak­ticky odpadá nut­nost psát kla­sické for/fo­re­ach/while cykly.

Z ukázky je pak patrná další věc a tou jsou nové li­te­rály pro ko­lekce. V Groovy se Arra­y­List vy­tvoří velice snadno:

def list = [1, 2, 3, "string", null, new Object()]

Aso­ci­a­tivní pole se vy­tvoří také velice jed­no­duše:

def map = [ klic1: "Anomalocaris Detrimentum", klic2: "2084", klic3: "Cerv" ]

Klíče jsou au­to­ma­ticky po­va­žo­vány za ře­tězce i když kolem sebe nemají žádné uvo­zovky nebo apo­strofy. Když chceme, aby byl klíč vy­hod­no­cen jako pro­měnná, jed­no­duše kolem něj dáme zá­vorky (je to sice ne­kon­zis­tentní, ale na druhou stranu to re­flek­tuje sku­teč­nost, že v 99% pří­padů chceme, aby klíčem byl ře­tě­zec).

ope­rá­tory

Groovy do jazyka při­dává ně­které uži­tečné nové ope­rá­tory, které o trochu zjed­no­duší život.

?. Safe Na­vi­gation Ope­ra­tor – je ekvi­va­lentní s nor­mál­ním teč­ko­vým ope­rá­to­rem pro pří­stup k me­to­dám nebo pro­měn­ným, ale když je pomocí něho volána metoda na nulové re­fe­renci, zamezí vy­ho­zení výjmky Null­Poin­te­rEx­cep­tion, ale pouze vrátí null – s jeho pomocí je možné bez­pečně volat něco ta­ko­vého a ne­sta­rat se nulové re­fe­rence

user?.address?.steet

*. Spread Ope­ra­tor – Toto je velice uži­tečný ope­rá­tor, který nad každým prvkem ko­lekce pro­vede danou ope­raci (volání metody, pří­stup k pro­perty) a vý­sle­dek vrátí v ko­lekci. Jde o další způsob, jak se vy­hnout psaní cyklů.

parent*.action                  //je ekvivalentní s
parent.collect{ it?.action }

def names = persons*.name       //vrátí kolekci jmen

Když to po­rov­náme s nej­ú­spor­něj­ším ře­še­ním v Javě:

ArrayList names = new ArrayList();
for (Person p : persons) {
	names.add(p.getName())
}

je jasně vidět, co je struč­nější.

persons*.address*.city          //lze provést i vícenásobně, protože se vždycky vrátí objekt List
myObjects*.toString()           //na každém objketu zavolá toString() a vrátí kolekci

?: elvis ope­rá­tor – jde o kom­pakt­nější verzí ter­nár­ního ope­rá­toru a fun­guje tak, že když je výraz před El­vi­sem vy­hod­no­cen jako true, pak se vrátí sa­motný výraz, když jako false vrátí se to, co se na­chází za El­vi­sem

def whoIsElvis = elvis ?: defrauder

//ekvivalentní s

def whoIsElvis = elvis ? elvis : defrauder

Re­gu­lární výrazy

Re­gu­lární výrazy byly za­čle­něny do jazyka na syn­tak­tické úrovni pomocí tří nových ope­rá­torů:

~string             // vytvoří Pattern ze stringu
string =~ pattern	  //hledá vzor ve stringu (find)
string ==~ pattern  //hledá přesnou shodu (match)

Ve spo­jení s uzá­vě­rami je práce s regexy ještě snad­nější a pře­hled­nější:

def dates = []
def line = "Today is 31.01.2007, 15:01. Tomorrow is 01.02.2007, 15:01"

(line =~ /(\d\d.\d\d.\d\d\d\d), (\d\d:\d\d)/).each { all, date, time ->
	//každý výskyt regulárního výrazu je předán jako parametr pro tuto closure
    dates << date
}

Roz­sahy

Roz­sahy jsou další uži­teč­nou vlast­ností Groovy. Jed­no­duše re­pre­zen­tují rozsah hodnot od A do B.

def range1 = 2..10   //rozsah 2 až 10
def range2 = 2..<10  //rozsah 2 až 9

Roz­sahy se dají použít k vy­tvo­ření pod­ře­tězců string[1..4] nebo stej­ným způ­so­bem k vy­tvo­ření pod­mno­žin Arra­y­Listu. Rozsah můžeme použít ve swit­chi:

switch (someValue) {
    case 1..3 : feedback = 'quite ok'; break
    case 3..6 : feedback = 'needs improvement'; break
    default: feedback = 'unknown'
}

nebo v cyklu

for (i in range) {
    out << i
}

Cykly

A tím se do­stá­váme k cyklům, které jsou silně in­spi­ro­vány Ruby.

Cyklus můžete vy­tvo­řit úplně jed­no­duše:

8.times {
	print "průchod číslo ${it+1}"
}

Nebo pomocí roz­sahu:

(4..8).each {
	println it
}

Mož­ností je hodně a jejich kód je velice samo-po­pisný. Groovy do­konce kla­sické for-cykly ne­pod­po­ruje, ale jak je vidět, nejsou třeba.

Ob­jekty

Na rozdíl od Javy, která má pri­mi­tivní typy, je v Groovy všechno objekt. Když k tomu při­poč­teme, že po­u­žití ope­rá­toru je jenom mas­ko­vané volání funkce, do­stá­váme mož­nost pře­tě­žo­vat ope­rá­tory. To vy­u­žívá jmen­nou kon­venci (ope­rá­tor + volá metodu plus(), – volá minus(), << volá leftShift() atd)

Všechny ope­rá­tory jsou hojně vy­u­ží­vány ve stan­dardní knihovně, která je roz­ší­řena o mnoho metod.

Např: << stan­dardně pro­vede při­po­jení ele­mentu do ko­lekce:

collection = []
collection << "elem"
collection << 1

Dále se jako false vy­hod­no­cují obecně všechny ob­jekty, které jsou po­va­žo­vány za prázdné, tedy prázdné ko­lekce, prázdné stringy, null a číslo 0.

Pro­perty

Dalším syn­tak­tic­kým cukrem jsou pro­perty. Jde o zjed­no­du­šený způsob volání get­terů a setterů již exis­tu­jí­cích ja­vov­ských tříd.

def x = obj.someting   // ekvivalentní s obj.getSomething()
obj.someting = x       // ekvivalentní s obj.setSomething(x)
píše k47 & hosté, ascii@k47.cz