Kontrolery w grails

Duża porcja wiedzy o kontrolerach na podstawie książki Definitive guide to grails.

Kontrolery w grails są odpowiedzialne za przechwytywanie requstów aplikacji i decydowanie co z nimi można zrobić:

  • wykonać inna akcję kontrolera (nie koniecznie tego samego)
  • wyświetlić widok
  • wyświetlić informację bezpośrednio w responsie

Klasy z kontrolerami znajdują się w katalogu controllers. Każda klasa musi się kończyć na Controller (konwencja). Kontrolery nie potrzebują rozszerzać jakiejś klas bądź implementować specyficznych interfejsów.

Akcje reprezentowane są jako pola do których dopisane jest domknięcie. W URLach dostęp jest przez konwencję, np /sample/action, gdzie sample to nazwa kontrolera (bez słowa Controller) a action to zdefiniowana akcja. W kontrolerze może być zdefiniowanych wiele akcji.

Jeśli nie jest podana żadna akcja, to jest wykonywana akcja domyślna:

  • jeśli kontroler posiada jedną akcję, to ona jest domyślna
  • jeśli kontroler posiada akcję index to ona jest domyślna
  • jeśli posiada właściwość defaultAction, to akcja przypisana do tego pola jest domyślna

Do każdego kontrolera jest wstrzyknięta właściwość log, która jest instancją org.apache.commons.logging.Log

Oprócz log do kontrolera są wstrzyknięte inne pola, jak między innymi HttpServletRequest, HttpServletResponse itd:

  • actionName – nazwa wykonywanej akcji
  • actionUri – URI do akcji
  • controllerName – nazwa kontrolera
  • controllerUri – URI kontrolera
  • flash – do pracy w zasiegu flash, równoznaczne z params tylko inny zasięg
  • log
  • params – mapa parametrów
  • request – HttpServletRequest
  • response – HttpServletResponse
  • session – HttpSession
  • servletContext – ServletContext

Dostęp do tych obiektów javie (za pomocą servletów)

request.getAttribute("myAttr");
request.setAttribute("myAttr", "myValue");

A w kontrolerach grails

request.myAttr
request.myAttr = "myValue"

Zasięgi (scopes):

  • request – obiekty utrzymują się tylko dla bieżącego żądania
  • flash – obiekty są dostępne dla obecnego i następnego żądania
  • session – są przetrzymywane do momentu aż sesja użytkownika zostanie unieważniona (ręcznie lub automatycznie)
  • servletContext – przetrzymywane przez cały czas życia aplikacji. W tym zasięgu obiekty nie są synchronizowane i należy dokonywać synchronizacji ręcznie, np
def index = {
  synchronized(servletContext) {
    def myValue = servletContext.myAttr
    servletContext.myAttr = "changed"
    render myValue
  }
}

Pola z formularzy są przekazywane do kontrolera i dostęp jest poprzez params

def userName = params.userName
log.info("User Name: ${userName}")

Poprzez metodę render można renderować tekst. Można dodać contentType:

render text:'<album>Revolver</album>', contentType:'text/xml'

Najczęściej używa się metody render do renderowia widoków i szablonów GSP.

Przekierowanie ządań odbywa się poprzez metodę redirect:

def first = {
  redirect(action: "second")
}

Metoda ta składa się z mapy, gdzie mogą występować różne klucze:

  • action – nazwa akcji do której następuje przekierowania
  • controller – nazwa kontrolera do którego następuje przekierowanie
  • id – id parametru do przekazania
  • params – mapa parametrów
  • uri
  • url

Domyślny widok dla kontrolera jest pobierany na podstawie akcji i nazwy kontrolera.
Można tworzyć widoki nie standardowe poprzez metodę render.

class SongController {
  def show = {
    render(view:"display",model:[ song: Song.get(params.id) ])
  }
}

widok znajduje się w grails-app/views/song/display.gsp, i zostaje przekazany model Song.
Widok może się znajdować w innym katalogu

render(view:"/common/song", model:[song: Song.get(params.id) ])

wskazuje na grails-app/views/common/song.gsp.
Renderowanie szablonów jest podobne

render(template:"/common/song", model:[song: Song.get(params.id) ] )

przez konwencję nazwy szablonów rozpoczynają się podkreśleniem(_). Szablon to mały wycinek kodu, który widoki mogą dołączać.

W konstruktorze można przekazać mapę parametrów, aby nie robić przypisania każdego parametru oddzielnie:

def album = new Album(params)

Dla istniejących obiektów można w zbliżony sposób przekazać parametry:

def album = Album.get(params.id)
album.properties = params

Walidacja przychodzących danych jest dwustopniowa. Najpierw jest sprawdzana zgodność typów. Jeśli pojawi się błąd obiektu nie można zapisać (read-only). Jeśli to przejdzie sprawdzane są walidatory.
Mechanizm walidacji opiera się na springowym pakiecie org. springframework.validation.

Z punktu widzenia kontrolera jeśli obiekt domenowy jest w nieprawidłowym stanie, należy w logice obsłużyć ten stan, np:

if(album.save()) {
  redirect(action: "show", id:album.id)
}
else {
  render(view: "edit", model:[album:album])
}

Można iterować po błędach obiektu:

object.errors.allErrors.each { println it.code }

Sprawdzić czy istnieją jakieś błędy:

object.hasErrors()

Rendedrowanie błędów w widoku to:

<g:renderErrors bean="${object}" />

Nazwy parametrów, przekazanych w params, są pobierane na podstawie atrybutu name w html, np

<input type="text" name="title" />

Jeśli, należy przekazać parametry do więcej niż jednego obiektu to należy w name dodać jakiś klucz (przestrzeń nazw).

<input type="text" name="album.title" />
<input type="text" name="artist.name" />

I pobranie odpowiednich parametrów to:

def album = new Album( params["album"] )
def artist = new Artist( params["artist"] )

Można zawężać przekazane parametry przy wykorzystaniu metody blindData:

bindData(album, params, [include:"title"])

Można tak zawężać do wielu obiektów, gdzie ostatnim argumentem jest klucz:

bindData(album, params, [include:"title"], "album")

CommandObject, jest to klasa, która ma właściwości klasy domenowej ale nie jest trwała (persistent).
Obiekty żądania definiuje się w katalogu contollers lub w tym samym pliku co kontroler.

Można ograniczyć wywołanie metody Http.POST(Http.GET itd) do konkretnych akcji:

class SomeController {
// action1 may be invoked via a POST
// action2 has no restrictions
// action3 may be invoked via a POST or DELETE
def allowedMethods = [action1:'POST', action3:['POST', 'DELETE']]
def action1 = { ... }
def action2 = { ... }
def action3 = { ... }
}

Jeśli metoda zostanie wywołana w niedozwolonym trybie zostanie rzucony błąd 405 – „method not allowed”

Formatka załączania plików:

<g:uploadForm action="upload">
<input type="file" name="myFile" />
<input type="submit" value="Upload! " />
</g:uploadForm>

obsługa tego:

def upload = {
  def file = request.getFile('myFile')
  // do something with the file
}

Grails automatycznie potrafi rozpoznać byte[] i odpowiednio połączyć z odpowiednią zmienną.

grails-app/views/layouts/main.gsp jest dostępne na każdej stronie.

Scaffolding w grails i nie tylko

Uzupełnienie mojej wczorajszej notki o wstępie do grails ale na podstawie książki Definitive guide to grails panów Greame Rocher i Jeffa Brown. Niby prawie to samo co w Beginning Groovy and Grails: From Novice to Professional ale podane w inny sposób i trochę rozszerzające wiedzę.

Netbeans nie wspiera tworzenia pakietów dla groovy/grails. Należy użyć prawy na projekcie i run grails command i przykładowo przy tworzeniu klasy domenowej podać nazwę pakietu.

Groovy automatycznie rozpoznaje nazwę klasy jako instancję java.lang.Class czyli Song jest równoważne Song.class

Aby utworzyć relację dwukierunkowa należy w jednej klasie(Obiekt2) dodać relację
static hasMany = [obiekty:Obiekt1], a w klasie Obiekt1 utworzyć pole Obiekt2.

Jeśli przy scaffoldingu pojawia się wyjątek:
Cannot generate controller logic for scaffolded class class package.ClassName. It is not a domain class.
i mamy całkowitą pewność, że wszystko jest dobrze (pakiety i nazwy klas) to należy wykonać clean w projekcie i zatrzymać serwer i dodatkowo zrestartowałem ide i po tym problem zniknął.

Scaffolding dzieli się na dynamiczny i statyczny. Dynamiczny używa refleksji i metaprogramowania groovy, tworzy widok podczas wykonywania. Jego użycie to w klasie kontrolera utworzenie akcji

def scaffold = DomainClass

Dynamiczny scaffolding jest zazwyczaj bezużyteczny jeśli jest potrzebna jakaś kustomizacja (np wygląd). Dobry jest do prototypowania.

Statyczny scaffolding tworzy się za pomocą poleceń.
generate-controller-generuje kontroler na podstawie klasy domenowej. Zawarte w nim jest kilka metod:

  • index – domyślna akcja dla kontrolera, wywoływana jeśli zadna inna nie została wywołana, przekierowuje na akcję list z dowolnymi parametrami.
  • list – wyświetla listę obiektów.
  • show – pobiera id obiektu i pokazuje go.
  • delete – usuwa obiekt.
  • edit – przekierowanie na stronę edycji (podobna do show)
  • update – aktualizacja
  • create – przekierowuje na stronę tworzenia nowego obiektu
  • save – zapisuje

Nazwy akcji określają jaka strona dla danego kontrolera się pojawi(konwencja ponad konfigurację).
W statycznym scaffoldingu trzeba wygenerować widok za pomocą polecenia:

generate-views DomainClass

To polecenie generuje cztery strony gsp:

  • list
  • show
  • edit
  • create
generate-all DomainClass

generuje kontroler i widok.

W statycznym scaffoldingu można wprowadzać własną logikę biznesową, własny wygląd.

redirect tworzy nowy request dla akcji, a render tworzy widok dla bieżącego requesta.

Grails zapewnia 3 domyślne profile pracy (również można tworzyć własne)

  • produkcyjny
  • rozwojowy (developerski)
  • testowy

Uruchomienie danego profilu z poleceniem to [profil] [polecenie] [argumenty]
prod, test, dev – oznaczają odpowiedni profil.

W klasie configuration/DataSource.groovy znajduje się konfiguracja z bazą danych (są tam profile pracy).

run-war pakuje aplikacje do wara i uruchamia serwer jetty.
war tworzy wara aplikacji(domyślnie profil produkcyjny), można dodać na końcu ścieżkę gdzie ma się znajdować war. Domyślnie jest on w roocie aplikacji.
Deployment odbywa się do wara i wgranie na serwer jak zwykły war, w zależności od danego serwera.

Parę słów o grails

Do zakończenia książki Programming Groovy: Dynamic Productivity for the Java Developer zostało mi kilka rozdziałów, które nadrobię w jak ta wiedza mi będzie potrzebna. Ale, że groovy to dopiero pierwszy krok, więc teraz pora na grails. Ten wpis będzie oparty na książce Beginning Groovy and Grails: From Novice to Professional a kolejne na The Definitive Guide to Grails z powodu tego, że ta druga korzysta z wersji 1.1. A między wersjami są pewne różnice.

Do bardzo szybkiego startu można skorzystać z tego tutorialu, dzięki zostanie stworzona bardzo ale to bardzo prosta, funkcjonalna aplikacja CRUD.

W grails mamy do czynienia z konwencja ponad konfigurację (convention over configuration) i wzorcem mvc (model to klasy domenowe, view – gsp).
Grails zapewnia z miejsca serwer, bazę danych, system budowania i testy.
scaffolding – tworzenie CRUD bardzo małym nakładem kodu, tak jak w powyższym tutorialu.
GORM, pluginy, agile są bardzo mocno powiązane z grails.

Grails nie wymyśla koła na nowo, tylko integruje w sobie wiele rozwiązań:
groovy
spring
hibernate
siteMesh
ajax(script.aculo.us, rico, prototype)
jetty
hsqldb
junit
gant

Tworzenie aplikacji używając scaffoldingu można sprowadzić się do następujących kroków:
1.Utworzenie aplikacji
2.Uruchomienie
3.Stworzenie klas domenowych
4.Implementacja testów
5.Uruchomienie testów i poprawka klas domenowych
6.Stworzenie kontrolera
7.Powtarzanie kroków 3-6 do momentu aż domeny i kontrolery będą kompletne a testy będą przechodzić.

Stworzenie aplikacji:

create-app projectName

W netbeansie 6.7.1 file/new project/groovy/grails, w następnym oknie podając nazwę i lokalizację. Wcześiej należy skonfigurować ścieżkę do grails.
Tworzy się nowy katalog, jeśli operacje są wykonywane z terminala to polecenia należy wykonywać z poziomu tego katalogu (root aplikacji). Struktura katalogów, jest standardowa, i każdy ma swoją rolę.

Uruchomienie aplikacji (serwera):

run-app

(netbeans prawy na nazwie projektu i run)

Tworzenie klasy domenowej

create-domain-class clasName

(prawym na domainClasses, new i Grails domain class). Zostaje utworzona klasa oraz klasa testowa.
Uruchomienie testów

test-app

(prawy na projekcie i test). Raport znajduje się w test\reports\html\.

Usuwanie wszystkiego to

Obiekt.list()*.delete()

Uwaga list ładuje wszystkie obiekty z bazy, więc mogą pojawić się problemy z pamięcią.
Istnieją rozszerzenia assertów np assertToString().
Id i version nie są widoczne w klasie domenowej. Są tworzone domyślnie.

Constrainty dla pól w klasie domenowej tworzy się poprzez:
static constraints = {
name(blank:false)
createdDate()
priority()
status()
note(maxSize:1000, nullable:true)
completedDate(nullable:true)
dueDate(nullable:true)
Jednocześnie ograniczenia mogą reprezentować elementy w html (maxSize to textarea a sam String to input) i są pokazywane na stronie w takiej kolejności jak są wpisane w ograniczenia. Walidacja i komunikaty pojawiają się jeśli ograniczenie jest złamane.

Tworzenie kontrolera

create-controller name

(prawy na kontrolerze).

Użycie scaffoldingu to

def scaffold = Klasa domenowa

Klasa nie może się nazywać Category. Powoduje to błąd

ERROR plugins.DefaultGrailsPlugin  - Cannot generate controller logic for scaffolded class interface groovy.lang.Category. It is not a domain class!

Ewentualnie można użyć tej klasy razem z pakietem. W wersji grails 1.1 doszła klasa Category w pakiecie groovy.lang, a ten pakiet jest domyślnie zaimportowany. Należy wyczyścić zawartość docelowego katalogu (tam gdzie jest to kopiowane u mnie: C:\Documents and Settings\User\.grails\1.1.1\projects\collab-todo [polecenie clean]

static belongsTo = [User, Category]

belongsTo oznacza, że dany obiekt zostanie usunięty jeśli powiązane obiekty zostaną usunięte (np User lub Category).
W oknie edycji/tworzenia nowego obiektu te pola są reprezentowane jako selecty a wyświetlone są za pomocą metody toString().

static hasMany = [todos:Todo]

hasMany – relacja jeden do wielu, dostęp do właściwości poprzez klucz (tutaj todos)

Uzupełnienie o scaffolding w grails.

Domknięcia w groovy

Bardzo ciekawy rozdział książki Programming Groovy: Dynamic Productivity for the Java Developer i chyba jeden z najważniejszych a mianowicie domknięcia w groovy (closures in groovy).

Czym jest domknięcie (closures)? Jak mówi oficjalna strona domknięcie jest to anonimowy łańcuch kodu, który może przyjmować argumenty, zwracać wartości oraz wskazywać i używać zmiennych będących w zasięgu domknięcia. Domknięcia są podobne do anonimowej klasy wewnętrznej w javie, ale posiadają większe możliwości.

Zawsze zwracają wartość (wartość ostatniego wyrażenia jeśli nie ma return).
Domknięcia posiadają jedną metodę.

GDK rozszerza JDK o metody, które przyjmują domknięcia.
Domknięcia nie mogą być samodzielne, muszą być podpięte do zmiennej lub metody.

Jeśli w domknięciu występuje jeden parametr można się odwołać do niego poprzez specjalną zmienną it:

pickEven(10) { println it }

Można zmienić nazwę tej zmiennej:

pickEven(10) { evenNumber -> println evenNumber }

Porównanie wykonania tej samej czynności w sposób javowy i za pomocą domknięć w groovy.
Tradycyjne (javove) podejście:

def sum(n)  { 
  total = 0
  for(int i = 2; i <= n; i += 2) {
    total += i
  }
  total
}
println "Sum of even numbers from 1 to 10 is ${sum(10)}"
&#91;/sourcecode&#93;

i w stylu groovy:
&#91;sourcecode language='java'&#93;
def pickEven(n, block) {
  for(int i = 2; i <= n; i += 2) {
    block(i)
  }
}
total = 0
pickEven(10) { total += it }
println "Sum of even numbers from 1 to 10 is ${total}"
&#91;/sourcecode&#93;

Domknięcia są funkcją, których zmienne są powiązane z kontekstem lub środowiskiem w którym są wywoływane. Są jedną z najbardziej potężnych rzeczy w groovy i mają elegancką składnię.

W C są wskaźniki, w javie anonimowe klasy wewnętrzne (wiążą z interfejsem) a w groovy domknięcia. Dzięki temu można bardzo prosto zaimplementować wzorzec strategia np:
&#91;sourcecode language='java'&#93;
def totalSelectValues(n, closure){
  total = 0
  for(i in 1..n) {
    if (closure(i)) { total += i }
  }
  total
}
print "Total of even numbers from 1 to 10 is "
println totalSelectValues(10) { it % 2 == 0 }
print "Total of odd numbers from 1 to 10 is "
println totalSelectValues(10) { it % 2 != 0}
&#91;/sourcecode&#93;

Domknięcia można przypisywać do zmiennych i używać ponownie także można do konstruktora dodać domknięcie. Kolekcje wykorzystają domknięcia.

Jeśli w domknięciu występuje jeden parametr nie trzeba go wymieniać (domyślna nazwa it), jeśli jest ich więcej trzeba je wylistować:
&#91;sourcecode language='java'&#93;
def tellFortune(closure){
  closure new Date("11/15/2007" ), "Your day is filled with ceremony"
}
tellFortune() { date, fortune ->
  println "Fortune for ${date} is '${fortune}'"
}

-> oddziela deklarację parametrów w domknięciu od ciała domknięcia. Możliwe jest nadanie typów parametrom.

Jeśli operujemy na zasobach (np plikach), gdzie jest otwieranie, jakaś czynność, zamykanie to można w prosty sposób utworzyć metodę, która jako parametr przyjmuje domknięcie (blok z jakąś czynnością) a sam zadba o otwarcie i w bloku finally zamknie. Dzięki temu można się skupić bardziej na tym co dany zasób ma robić a nie na całej otoczce

def static use(closure) {
  def r = new Resource()
  try {
    r.open()
    closure(r)
  }
  finally{
    r.close()
  }
}

i tego wywołanie:

Resource.use { res ->
res.read()
res.write()
}

Pamiętane są dane z poprzednich wywołań (sekwencyjne). Domknięcia dają wrażenie coroutines (jakie może być tego tłumaczenie?) w pojedynczych wątkach.

Istnieje metoda curry, która wiąże wartość parametru z domknięciem. Przypisać tak, można wiele parametrów ale w kolejności od pierwszego. Pozwala to zlikwidować duplikację i redundancję kodu i wywoływanie domknięć z wieloma powtarzającymi się parametrami.

Można sprawdzić czy domknięcie jest przekazane i na podstawie tego zdecydować co zrobić:

if (closure && closure instanceof Closure) { return closure() }
println "Using default implementation"

Warto dodać sprawdzenie czy to jest naprawdę domknięcie przez instanceof bo przy takim wywołaniu:

function "Some words"

, poleci MissingMethodException, to rozwiązanie znalezione u Jacka Laskowskiego.

Można sprawdzać ilość przekazanych parametrów w domknięciu za pomocą pola maximumNumberOfParameters oraz typ parameterTypes.
{} i {it} mają jeden parametr, który wskazuje na null
{-> } nie posiada parametrów (0 parametrów).
Na podstawie tych 2 pól można zapewnić różne implementacje.

this, owner i delegate – wywołanie metody w ramach domknięcia to przekazanie ich do obiektu this a później delegate. Delegate domyślnie wskazuje na ownera.

Kiedy używać domknięć?:
-przy warunkach lub predykatach przy których następuje wybór obiektów
-przy kontrolowanych przepływach np iteratory (coroutines)
-czyszczenie zasobów
-tworzenie dsl

Dla konkretnych i dobrze sprecyzowanych zadań lepiej używać zwykłych funkcji, domknięcia można wprowadzać podczas refaktoryzacji.
Domknięcia powinny być małe i spójne (kilka linii), nie należy nadużywać dynamicznych właściwości domknięć.

Dynamiczne typowanie w groovy

Venkat Subramaniam w czwartym rozdziale swojej książki Programming Groovy: Dynamic Productivity for the Java Developer przedstawia dynamiczne typowanie.

Zalety dynamicznego typowania:
-Pozwala osiągnąć większy stopień polimorfizmu niż inne języki statycznie typowane
-Nie trzeba rzutować na inne obiekty

Dynamiczne typowanie jest czymś innym niż słabe typowanie.

Domyślnie każdy obiekt jest typu Object.

Projektowanie przez zachowanie/możliwości – design by capability.
W statycznym typowaniu należy tworzyć klasy nadrzędne/interfejsy i przy dodaniu nowej rzeczy więcej problemów z rozbudową, przy dynamicznym jeśli obiekt posiada daną metodę to znaczy ze jest „czymś”(zachowuje się jak „coś”).

„Jeśli coś chodzi jak kaczka i kwacze jak kaczka to jest kaczką – duck typing.

Dynamiczne typowanie wymaga dużej dyscypliny, wręcz wskazane są testy jednostkowe (junit) oraz dobra konwencja nazewnicza.

if (obiekt.metaClass.respondsTo(obiekt, 'methodName' ))

można sprawdzić czy obiekt posiada daną metodę, metoda bezpieczna zawsze zwraca wartość.

Groovy jednocześnie jest opcjonalnie typowanay, zapewnia to integrację z innymi bibliotekami, frameworkami i narzędziami.
Jeśli przed metodą jest def, to zwraca ona Object.
Nie ma typów prymitywnych, wszystko to obiekt.

Działania matematyczne http://groovy.codehaus.org/Groovy+Math,
2**3 –> 2^3 (2 do 3 potęgi), Liczby z przecinkiem są traktowane jako java.math.BigDecimal, znika problem z resztami na ostatnich bitach. Można podać typy zmiennoprzecinkowe znane z Javy (Float, Double).

MultiMetody:

public class Employee {
  public void raise(Number amount){
    System.out.println("Employee got raise" );
  }
}

public class Executive extends Employee{
  public void raise(Number amount){
     System.out.println("Executive got raise" );
  }
  public void raise(java.math.BigDecimal amount){
    System.out.println("Executive got outlandish raise" );
  }
}

import java.math.BigDecimal;
public class GiveRaiseJava{
  public static void giveRaise(Employee employee){
    employee.raise(new BigDecimal(10000.00));
  }
  public static void main(String[] args){
    giveRaise(new Employee());
    giveRaise(new Executive());
  }
}

wynikiem w javie jest:
Employee got raise
Executive got raise
W javie w wywołaniu metody klasy podrzędnej pod uwagę brane są takie parametry jak w klasie nadrzędnej – metoda dziedziczona.

void giveRaise(Employee employee){
  employee.raise(new BigDecimal())
// same as
//employee.raise(10000.00)
}
giveRaise new Employee()
giveRaise new Executive()

wynik dla tych samych klas Employee i Executive jak wyżej:
Employee got raise
Executive got outlandish raise

W groovy pod uwagę brany jest typ parametru, dzięki czemu można wywołać metodę przeciążoną (ciekawe zastosowaniu przy kolekcjach i listach).

Warto nadawać typy przy testach i przy mapowaniu(GORM) a także jeśli jest tworzone api dla kogoś kto używa statycznych języków.

Groovy start

W końcu zaczynam się brać za groovy a następnie za grailsy. Swoją wiedzę będę budował na podstawie książki Programming groovy i dokumentacji dostępnej na stronie. Wpisy nie będą miały formy żadnego tutorialu tylko moje luźne myśli, ku pamięci. Dodatkowo po każdym przeczytanym rozdziale będę odwiedzał wpisy Jacka Laskowskiego, aby sprawdzić czy coś mi nie umknęło.

Pierwszy a właściwie trzeci rozdział, w którym groovy jest porównany do kodu javowego i jego różnice.

Uruchomienie groovy:
groovysh, groovyConsole lub groovy -e nazwa klasy.
Nie są potrzebne średniki, zamiast pisać System.out.println wystarczy samo println (metoda dodana do Object – GDK).
przy princie (i innych metodach) nie trzeba dodawać nawiasów np println „Hello world”, także deklaracja klasy nie jest potrzebna.

pętla for(int i = 0; i<3; i++) jest równoważne :
– z for(i in 0..2)
– 0.upto(2) {domknięcie} – ustala dolny i górny limit
-można też 0.step(10,2) {domknięcie} gdzie iterujemy od 0 do 10 z krokiem 2

Bezpieczny operator chroni przed sprawdzaniem czy obiekt jest nullem:
obiekt?.metoda() jest równoznaczne if(obiekt!=null) {return obiekt.metoda()}

return jest opcjonalny, średnik opcjonalny, metody i klasy domyślnie publiczne, wyjatki są przekazywane wyżej

Jeśli jest tworzony skrypt i zawiera w sobie klasę, to nazwa tej klasy musi być inna niż nazwa pliku?

Gettery i settery są tworzone automatycznie (tak jak domyślny konstruktor w javie):
$obiekt.pole –> getPole
obiekt.pole = 25 –> setPole(25)
Jeśli pole jest zadeklarowane jako final to oznacza, że dostępny jest tylko getter (pole jest tylko do odczytu i nie można go zmienić z zewnątrz).

Można używać str.class.name zamiast str.getClass.getName (przy Map lepiej używać getClass).

Można tworzyć obiekty podając pary – nazwa pola: wartość, kolejne pary rozdzielane przecinkiem.
Do metod można przekazywać takie pary jako parametry, kolejność parametrów może zostać zmieniona. Jeśli do metody jest przekazanych więcej parametrów niż w sygnaturze i są one tymi parami, to wtedy pierwszy parametr w sygnaturze jest traktowany jako mapa i przypisywane są do niego te pary. Aby pierwszy parametr był zawsze traktowany jak mapa to należy w definicji zadeklarować Map

Parametry w metodach i konstruktorch mogą być opcjonalne;

 def log(x, base=10)
  {
      Math.log(x) / Math.log(base)
  }

i wywołanie log(1024) jest równoznaczne log(1024, 10)

Jeśli parametrem (ostatnim?) metody jest tablica to można nie podawać w ogóle tego parametru, lub dowolna ilość rozdzielaną przecinkiem.

Interfejsy są implementowane inline przez słowo as dla bloku kodu

displayMouseLocation = { positionLabel.setText("$it.x, $it.y" ) }
frame.addMouseListener(displayMouseLocation as MouseListener)
frame.addMouseMotionListener(displayMouseLocation as MouseMotionListener)

Także można tworzyć mapy gdzie kluczem jest nazwa metody a wartością ciało metody

handleFocus = [
    focusGained : { msgLabel.setText("Good to see you!" )
    focusLost : { msgLabel.setText("Come back soon!" ) }
]
button.addFocusListener(handleFocus as FocusListener)

Nie jest wymagana implementacja wszystkich metod interfejsu, (Jeśli coś będzie nie wykorzystane to nie tworzy się pustych stubów, jak taka metoda zostanie wywołana wtedy leci Null)
przez metode asType można dynamicznie podać interfejsy np:

handler.asType(Class.forName("java.awt.event.${event}" ))

Sprawdzanie warunków:
jeśli obiekt jest nullem to wtedy if(obj) przyjmuje wartość false, aby było true:
Typ Warunek dla true

Boolean true
Collection not empty
Character value not 0
CharSequence length greater than
Enumeration has more elements
Iterator has next
Number double value not 0
Map not empty
Matcher at least one match
Object[ ] length greater than
any other type reference not null

Przeładowywanie metod, można użyć w własnych klasach lista tutaj (zwlaszcza a.next() –> a++ i ++aa)

Autoboxing działa z palca, każdy typ prosty jest konwertowany do obiektu.

Pętla for-each for(String greet : greetings) –> for(greet in greetings)
int [] b jest równoważne z int…b w parametrach metod

Można tworzyć aliasy przy importach dla klas jak i statycznych importach dla metod

import static Math.random as rand
import groovy.lang.ExpandoMetaClass as EMC
double value = rand()
def metaClass = new EMC(Integer)
assert metaClass.getClass().name == 'groovy.lang.ExpandoMetaClass'

equals i == są takie same (jeśli klasa nie implementuje Comparable, w przeciwnym wypadku == mapowany jest do compareTo) metoda is jest rownóznaczna z == w javie.

Każda operacja przypisania jest równoznaczna z rzutowaniem: x = y –> x = (ClassOfX)(y).
Nie ma bloków w kodzie, traktowane jak domknięcia
Średniki nie są wymagane oprócz sytuacji def v = 3 i dalej domknięcie, wtedy po zmiennej jest potrzebny średnik.
Tablica w groovy to int [] array = [1, 2, 3] (styl javowy int [] array = {1, 2, 3} nie zadziała)

%d blogerów lubi to: