Początek rozdziału to kolejne obietnice wręcz natychmiastowego zwiększenia produktywności z Groovy, bo nie tylko, że mamy większą dynamikę języka, ale i rozszerzone biblioteki bazowe Javy (Java API). Metody znane z relacji rozdziału o kolekcjach (Z rozdziału 7. o kolekcjach (listach) z "Programming Groovy" oraz Dokończenie rozdziału 7. "Working with Collections" z "Programming Groovy") - each(), collect(), find(), findAll(), any() oraz every() są faktycznie dostępne w każdej klasie javowej. To zdecydowanie zmienia nasze postrzeganie, czego możnaby oczekiwać więcej od java.lang.Object. Niektórych przyprawi to o ból głowy, że i sama Java wymaga wiele nauki, a tu proszę, Groovy jeszcze dodaje swoje trzy grosze. Nie ma lekko. Jeśli pozwolimy sobie na większą elastyczność jest pewnym, że członkowie zespołu projektowego podziękują nam za inspirujące pomysły (wierzę, że poznanie jakiegokolwiek nowego języka to wkroczenia na teren niedostępny innym, którzy nim nie władają - czy to język mówiony, czy programowania).
Nowymi metodami, których autor wcześniej nie przedstawiał, są dump() oraz inspect().
groovy:000> class Osoba {Za pomocą dump() możemy spojrzeć na strukturę i dane egzemplarza lub też samego typu. Wszystko, co może być przydatne w trakcie śledzenia działania aplikacji, odnotowania w logach (dziennikach zdarzeń) czy po prostu nauki. Metoda inspect() pozwala nam poznać, co jest potrzebne do utworzenia obiektu danego typu. Jak na razie nie zachwyciła mnie (pewnie jeszcze nie zrozumiałem sensu jej istnienia).
groovy:001> String imie, nazwisko
groovy:002> }
===> true
groovy:000> jacek = new Osoba(imie: 'Jacek', nazwisko: 'Laskowski')
===> Osoba@1382926
groovy:000> jacek.dump()
===> <Osoba@1382926 imie=Jacek nazwisko=Laskowski>
groovy:000> jacek.inspect()
===> Osoba@1382926
Miłośnicy JavaScript czy VBScript będą uradowani dowiadując się, że znana im metoda with() jest również dostępna w Groovy. Służy ona do określenia kontekstu wykonania metod w ramach przekazanego do niej domknięcia (przypomina mi to maćkowe dywagacje w Ograniczony kontekst). Zamiast serii metod na danym egzemplarzu, co w Javie wymagałoby podawania go każdorazowo, w Groovy można krócej. Jej aliasem jest identity() i wywołanie obu zwraca ten sam rezultat. Przykład od razu wyjaśni o co chodzi.
groovy:000> lista = [1,2]Zamiast każdorazowego określania egzemplarza, który ma być odbiorcą wywołania serii metod, wystarczy with() z domknięciem. W ramach tego domknięcia wszystkie metody będą kierowane do tego samego obiektu - obiektu, na którym wywołaliśmy metodę with() (lub identity()). Działanie with() polega na właściwym ustawieniu delegate domknięcia.
===> [1, 2]
groovy:000> lista.add(3)
===> true
groovy:000> lista.add(4)
===> true
groovy:000> lista
===> [1, 2, 3, 4]
groovy:000> println lista.size()
4
===> null
groovy:000> println lista.contains(2)
true
===> null
groovy:000> // a teraz prosciej
===> true
groovy:000> nowaLista = [1,2]
===> [1, 2]
groovy:000> nowaLista.with {
groovy:001> add(3)
groovy:002> add(4)
groovy:003>
groovy:003> println size()
groovy:004> println contains(2)
groovy:005> }
4
true
===> null
groovy:000> nowaLista
===> [1, 2, 3, 4]
Metoda sleep() jest udoskonaleniem znanej z Java API metody Thread.sleep(). Ignoruje ona przerwania (wywołanie Thread.interrupt()) przez zadany czas. Parametrem wejściowym sleep() jest domknięcie, które obsługuje przerwanie i które otrzymuje na wejściu InterruptedException. Jeśli domknięcie zwróci false wykonanie sleep() będzie trwało do zadanego czasu, jakby przerwanie nie nastąpiło.
groovy:000> def prezentacjaSleep(flag) {Zwróć uwagę na czasy wykonania wątków (oraz nową metodę w klasie Thread start()).
groovy:001> thread = Thread.start {
groovy:002> println "Watek rozpoczal sie"
groovy:003> poczatek = System.nanoTime()
groovy:004> new Object().sleep(3000) {
groovy:005> println "Obsluga przerwania z wyjatkiem: " + it
groovy:006> flag
groovy:007> }
groovy:008> koniec = System.nanoTime()
groovy:009> println "Koniec pracy watku - patrz na czasy wykonania - ${(koniec - poczatek)/10**9} sekund"
groovy:010> }
groovy:011> thread.interrupt()
groovy:012> thread.join()
groovy:013> }
===> true
groovy:000> prezentacjaSleep(true)
Watek rozpoczal sie
Obsluga przerwania z wyjatkiem: java.lang.InterruptedException: sleep interrupted
Koniec pracy watku - patrz na czasy wykonania - 0.001169143 sekund
===> null
groovy:000> prezentacjaSleep(false)
Watek rozpoczal sie
Obsluga przerwania z wyjatkiem: java.lang.InterruptedException: sleep interrupted
Koniec pracy watku - patrz na czasy wykonania - 3.00024193 sekund
===> null
Za pomocą operatora tablicowego [] (obsługiwany przez metodę getAt() lub putAt() w zależności od trybu - odczyt/zapis) możemy dynamicznie dostać się do właściwości obiektu. Zamiast statycznego (w sensie podania go w trakcie pisania tej konstrukcji, a nie kwalifikatora static) wywołania egzemplarz.wlasciwosc, można również dynamicznie - egzemplarz['wlasciwosc']. Zaletą takiego wywołania jest możliwość odczytu/zapisu właściwości nie znając jej nazwy podczas pisania aplikacji, np. po określeniu jej przez użytkownika.
groovy:000> jacek.dump()Wystarczy teraz użyć kolejnej metody getProperties() (albo prościej properties), aby dostać się do atrybutów danego typu i wykonać na nich pewne czynności.
===> <Osoba@1382926 imie=Jacek nazwisko=Laskowski>
groovy:000> jacek.imie
===> Jacek
groovy:000> jacek.nazwisko
===> Laskowski
groovy:000> dostepneWlasciwosci = ['imie', 'nazwisko']
===> [imie, nazwisko]
groovy:000> dostepneWlasciwosci.each { nazwa ->
groovy:001> println "${nazwa} = ${jacek[nazwa]}"
groovy:002> }
imie = Jacek
nazwisko = Laskowski
===> [imie, nazwisko]
groovy:000> jacek[dostepneWlasciwosci[0]] = 'JACEK'
===> JACEK
groovy:000> jacek.dump()
===> <Osoba@1382926 imie=JACEK nazwisko=Laskowski>
groovy:000> jacek.properties.each { prop -> println "$prop" }Oczywiście w samej Javie też to jest możliwe z wykorzystaniem Reflection API, ale prostota takiego podejścia jest nie do przecenienia. Do tego należy dodać, że wykonanie metod dynamicznie jest równie proste z invokeMethod() (autor dosyć sarkastycznie wprowadza w temat i szkoda byłoby zepsuć Wam poznanie tego osobiście - zapraszam na stronę 139. książki).
class=class Osoba
imie=JACEK
nazwisko=Laskowski
metaClass=org.codehaus.groovy.runtime.HandleMetaClass@cf3539[groovy.lang.MetaClassImpl@cf3539[class Osoba]]
===> {class=class Osoba, imie=JACEK, nazwisko=Laskowski,
metaClass=org.codehaus.groovy.runtime.HandleMetaClass@cf3539[groovy.lang.MetaClassImpl@cf3539[class Osoba]]}
groovy:000> jacek.metaClass.pewnaMetodaZParametrami { String p1, int p2 -> println "Podano p1=$p1 i p2=$p2" }Wszystkie wymienione metody są dostępne dla java.lang.Object, a tym samym i dla wszystkich typów.
===> null
groovy:000> jacek.invokeMethod("pewnaMetodaZParametrami", ["Pierwszy", 2] as Object[])
Podano p1=Pierwszy i p2=2
===> null
W klasie Process dodano trzy atrybuty - in, out oraz err. Dają one dostęp do, odpowiednio, strumieni wejściowych, wyjściowych i błędów. Mamy również atrybut text, który zwraca całe wyjście z procesu. Odczyt standardowego strumienia błędów to err.text.
groovy:000> proces = "uname -a".execute()Możemy to samo otrzymać z proces.text. Pamiętajmy, że po wykonaniu polecenia proces kończy się i strumień zostaje zamknięty, więc ponowny odczyt kończy się zgłoszeniem wyjątku.
===> java.lang.ProcessImpl@f9104a
groovy:000> proces.in.text
===> CYGWIN_NT-5.1 work 1.5.25(0.156/4/2) 2008-06-12 19:34 i686 Cygwin
groovy:000> proces = "uname -a".execute()Komunikacja z procesem możliwa jest z operatorem lewego przesunięcia <<, np.
===> java.lang.ProcessImpl@486cdd
groovy:000> proces.text
===> CYGWIN_NT-5.1 work 1.5.25(0.156/4/2) 2008-06-12 19:34 i686 Cygwin
groovy:000> proces.text
ERROR java.io.IOException: Stream closed
at groovysh_evaluate.run (groovysh_evaluate:2)
...
groovy:000> p = "wc".execute()Wykonanie poleceń z parametrami to wykonanie execute() na liście napisów, gdzie pierwszy z nich jest poleceniem, a reszta parametrami.
===> java.lang.ProcessImpl@956254
groovy:000> p.out.withWriter {
groovy:001> it << "wc zlicza liczbe liter, slow i zdan\n"
groovy:002> it << "wiec powinnismy dostac 2 zdania i in.\n"
groovy:003> }
===> java.io.OutputStreamWriter@4d93e3
groovy:000> println p.text
2 14 74
===> null
groovy:000> command = ['c:/apps/groovy/bin/groovy.bat', '--version']Dobre, co?
===> [c:/apps/groovy/bin/groovy.bat, --version]
groovy:000> command.execute().text
===> Groovy Version: 1.6.3 JVM: 1.6.0_14
W Thread widzieliśmy już dodatek w postaci metody start(), która akceptuje domknięcie - zbiór metod uruchamianych w osobnym wątku. Jeśli chcemy stworzyć wątek-demona korzystamy z Thread.startDeamon() z domknięciem na wejściu. Tutaj autor przeszedł samego siebie z wyjaśnieniem różnicy między wątkiem normalnym a demonem (patrz przypis na stronie 142):
"A deamon thread quits if there are no active nondeamon threads currently running - kind of like employees who work only when the boss is around"
A Tyś demonem? :)
W pakiecie java.io również kilka rozszerzeń Groovy. W klasie File mamy eachFile() oraz eachDir() z ich wariacjami, które akceptują domknięcia do nawigacji w systemie plików. Odczyt zawartości pliku, dowolnego obiektu typu Reader czy InputStream to po prostu odczyt atrybutu text.
groovy:000> f = File.createTempFile("temp", ".txt")Automatyczna obsługa otwarcia i zamknięcia pliku możliwa jest metodą withStream(), której parametrem wejściowym jest domknięcie wykonywane z parametrem - obiektem InputStream. Podobnie jest z zapisem z withWriter().
===> c:\temp\temp1698516868251566991.txt
groovy:000> f << "jakis tekst\n"
===> c:\temp\temp1698516868251566991.txt
groovy:000> f << 'i jeszcze inny\n'
===> c:\temp\temp1698516868251566991.txt
groovy:000> f << 'Jeszcze kolejna linia\n'
===> c:\temp\temp1698516868251566991.txt
groovy:000> f.text
===> jakis tekst
i jeszcze inny
Jeszcze kolejna linia
groovy:000> f.eachLine { linia -> println "$linia" }
jakis tekst
i jeszcze inny
Jeszcze kolejna linia
===> null
groovy:000> println f.filterLine { it =~ /olejna/ }
Jeszcze kolejna linia
===> null
Kolekcje List, Set, SortedMap oraz SortedSet otrzymały metody asImmutable() do ich zmiany na obiekt niezmienny oraz asSynchronized() do utworzenia obiektu "wielowątkowego" (ang. thread-safe).
groovy:000> l = ['ala', 'ma', 'kota']Więcej o tych i innych zmianach na stronie Groovy JDK API Specification.
===> [ala, ma, kota]
groovy:000> l.class.name
===> java.util.ArrayList
groovy:000> l << 'nowy elment'
===> [ala, ma, kota, nowy elment]
groovy:000> L = l.asImmutable()
===> [ala, ma, kota, nowy elment]
groovy:000> L << "nie mozna dodawac nowych elementow!"
ERROR java.lang.UnsupportedOperationException: null
at groovysh_evaluate.run (groovysh_evaluate:2)
...
groovy:000> l << 'A do l wciaz mozna'
===> [ala, ma, kota, nowy elment, A do l wciaz mozna]
Brak komentarzy:
Prześlij komentarz