środa, 30 lipca 2008

Groovy w praktyce - part 4 - mapy 1/2

Mapa w Groovy jest reprezentowana przez zwykłą klasę Javy, używać ją można jak zwykłą mapę, klasę asocjacyjną, a nawet obiekt.
groovy> a = [:]
===> {}

groovy> a.getClass()
===> class java.util.LinkedHashMap

// iteracje po mapie mozna wykonac na kilka sposobow
groovy> a = [a:'aa',b:'bb']
===> {a=aa, b=bb}

groovy> a.each{println it}
a=aa
b=bb
===> {a=aa, b=bb}

groovy> a.each{println it.key}
a
b
===> {a=aa, b=bb}

groovy> a.each{println it.value}
aa
bb
===> {a=aa, b=bb}

groovy> a.each{ key,value -> println key + ' - ' + value }
a - aa
b - bb
===> {a=aa, b=bb}

// ustawianie i odczytywanie elementów jest proste,
groovy> a.a
===> aa
groovy> a.b
===> bb
groovy> a.c
===> null
// ale z powodu takiej konwencji a.class == null, dlatego wcześniej użyłem getClass()


// na mapie można bezpośrednio tworzyć nowy element w analogiczny sposób
groovy> a.c = 'cc'
===> cc
groovy> a
===> {a=aa, b=bb, c=cc}
groovy> a['d'] = 123
===> 123
groovy> a
===> {a=aa, b=bb, c=cc, d=123}

groovy> a.keySet()
===> [a, b, c, d]
groovy> a.values()
===> [aa, bb, cc, 123]

Klucz w mapie domyslnie jest traktowany jako string, chyba ze jest liczbą, wartością logiczną lub null.
groovy> b = [1:1,a:2]
===> {1=1, a=2}
groovy> b[1]
===> 1
groovy> b['a']
===> 2
groovy> b.'1'
===> null
groovy> b[a]
===> null

Jeśli chcemy jako klucz użyć coś innego niż liczbę czy string to możemy użyć notacji tabeli asocjacyjnej mapa[obiekt], albo nawiasów():
groovy> x = [1,2]
===> [1, 2]
groovy> y = [ (x):'abc' ] //dzieki nawiasom w tym przypadku kluczem będzie lista
===> {[1, 2]=abc}
groovy> y.keySet()*.getClass()
===> [class java.util.ArrayList]
groovy> y.each{println it.key[1]}
2
===> {[1, 2]=abc}

Jeśli mapa jest parametrem funkcji to większości przypadków można porzucić nawiasy []
groovy> f = {mapa -> mapa.values().each{ println it }}
===> groovysh_evaluate$_run_closure1@1e46a68
groovy> f([a:1])
1
===> [1]
groovy> f([a:1,b:2])
1
2
===> [1, 2]
groovy> f(a:1,b:2)
1
2
===> [1, 2]
nawet jeśli mapa nie jest jedynym parametrem
groovy> g = {mapa, foo -> println mapa}
===> groovysh_evaluate$_run_closure1@d480ea
groovy> g(a:1,2)
["a":1]
===> null
groovy> g(a:1,b:2,2)
["a":1, "b":2]
===> null

Języki dynamicznie z domknięciami mają ciekawą cechę, mapa odpowiada w pewnym stopniu obiektowi, np. w ActionScript 2 nie ma osobnej kolekcji "Mapa" - korzysta się z Obiektów i dynamicznie tworzy nowe pola wygląda to tak jak kod w 3 linijce.
groovy> a = [:]
===> {}
groovy> a.pole = 1
===> 1
groovy> a.funkcja = { x -> x * a.pole } // ! trzeba zapisać skąd pochodzi "pole", m.in. tym się różni od normalnego obiektu
===> groovysh_evaluate$_run_closure1@20f237
groovy> a.funkcja(2)
===> 2
groovy> a.pole = 2
===> 2
groovy> a.funkcja(2)
===> 4
groovy>

Teraz, chwila przerwy, by każdy czytelnik mógł się zastanowić jak proste jest tworzenie mocków w Groovy za pomocą takich opcji.




Istnieje jeszcze jedna specjalna klasa - Expando - która w pewien, jak na razie magiczny, sposób pozwala na bardziej naturalne tworzenie dynamicznych obiektów.
groovy> e = new Expando()
===> {}
groovy> e.pole = 1
===> 1
groovy> e.f = { x -> x * pole } // ! nie trzeba specyfikować z jakiego obiektu pochodzi pole, czyli to czego oczekujemy
===> groovysh_evaluate$_run_closure1@174aa60
groovy> e.f(2)
===> 2
groovy> e.pole = 2
===> 2
groovy> e.f(2)
===> 4

Przetestuj poniższy kod, zapamiętaj co się dzieje [ zakładam, że wiesz dlaczego ;) ]
a = [a:1]
e = new Expando()
e.a = 1
e.f = { x -> x * a }
e.f(2)

Więcej o mapach

Brak komentarzy: