Groovy and Grails
Le langage Groovy
Sur cette page vous découvrirez les particularités du langage groovy.Les chaînes de caractères
On distingue trois nouveaux types de chaines de caractères.
-
Les "slashy strings" permettent d'éviter les redondances de caractères d'échappements.
Ainsi l'expression /Hello "world"/ permet d'exprimer Hello "world". Cela peut être utile pour exprimer des expressions régulières.
-
Les "multiline strings" permettent d'interpréter des chaînes de caractères sur plusieurs lignes.
Elles sont délimitées par 3 guillemets successifs.
Le code suivant :println """ Hello ${firstname}
pourrait afficher :
${lastname} ! """Hello Dominique
Revuz !
-
Les "GString" désignent toutes les chaînes de caractères qui contiennent des variables à l'intérieur :
"Hello ${firstname} ${lastname} ! "
Constructeur
Voici un exemple de script groovy. On y voit la déclaration d'un Groovy Bean ainsi que deux utilisations du constructeur par défaut.
package fr.umlv.xpose.groovy;
class Person {
def firstname;
def lastname;
String toString(){
return firstname+" "+lastname;
}
}
def teacher = new Person(firstname:"Etienne" , lastname:"Duris");
HashMap<String,String> resquestParameters = new HashMap<String,String>();
resquestParameter.putAt("firstname", "Etienne");
resquestParameter.putAt("lastname", "Duris");
def teacher2 = new Person(resquestParameters);
println teacher; // Etienne Duris
println teacher2; // Etienne Duris
Dans chaque Groovy Bean, il existe un constructeur qui prend en argument une Map et qui va utiliser les
setters correspondant à chaque clé de la Map pour initialiser les champs du Groovy Bean.
Dans les deux exemples, on utilise une Map en argument. Dans le premier exemple, c'est une déclaration simplifiée de la Map.
Les Méthodes
Elles ne sont pas si différentes des méthodes que l'on connait en Java.
Les seules différences sont les suivantes :
- Elles ont toutes une visibilité "public" par défaut.
- Nous n'avons pas besoin de préciser le type de retour ni les types pour les arguments.
- L'objet retourné est celui de la dernière ligne de la méthode.
Une autre différence par rapport au Java est qu'il possible d'en mettre a l'extérieur des classes. Lorsque c'est le cas on les nomme "fonction" car elles se comportent comme telle.
Les Clôtures
Clôture vient du mot anglais "closure". On retrouve ce type de structure dans d'autres langages.
La clôture est un des éléments les plus intéressants dans le langage groovy. Cela ressemble à des méthodes, mais ça n'en est pas.
Une clôture est avant tout un objet issu d'une classe interne. Cet objet possède
une méthode call qui va exécuter toutes les instructions qui sont entre les accolades.
Voici un exemple de clôture sans argument :
def hello = { println "Hello world !" }
hello() // Hello world !
Avec des arguments :
def hello = { name1, name2, name3 -> println "Hello ${name1}, ${name2}, ${name3} !" }
hello("Martin", "Etienne", "DR") // Hello Martin, Etienne, DR !
hello "Martin","Etienne","DR" // Hello Martin, Etienne, DR !
Si on utilise une clôture avec des arguments, l'objet possèdera des champs pour chaque arguments.
Comme on peut le voir dans l'exemple précédant, les arguments sont déclarés avant les instructions
en les séparant par une flèche "->".
Comme pour les méthodes, à partir du moment où la clôture possède un argument, celle-ci peut être appelée sans les parenthèses.
Il est aussi possible de passer une clôture à une méthode. C'est d'ailleurs son intérêt principal.
L'utilité des clôtures est le même que les pointeurs de fonction en C, ou que les "delegates" en C#.
En voici un exemple :
class Student {
def name;
def doIt(argument){
argument(name);
}
}
def martin = new Student(name : "Martin");
martin.doIt {
studentName -> println "Hello ${studentName} !"
}
// Hello Martin !
Dans cet exemple, on peut voir que j'utilise la méthode "doIt" sans les parenthèses, en lui passant une clôture en argument.
Dans la méthode, j'utilise la clôture en lui passant un champ de la classe en argument.
Les Collections
En Groovy, l'utilisation des collections est simplifiée. On retrouve les trois collections les plus utilisées en Java :
- La List :
def myList = []
println myList.getClass().name // java.util.ArrayList
myList.add "Bob"
myList += "Bob"
myList += "Martin"
myList << "Homer"
myList -= "Martin"
println myList // [Bob, Bob, Homer]
Pour créer une liste il suffit d'utiliser des crochets.
Si on veut initialiser cette liste avec des objets, on les met entre les crochets en les séparant par des virgules.
Pour ajouter des éléments a une liste, on peut utiliser la méthode add ou utiliser d'autres opérateurs tel que le "left shift" ou l'opérateur d'incrémentation.
Pour supprimer un élément de la liste, on peut utiliser la méthode remove ou l'opérateur de décrémentation. - Le Set :
def mySet = ["Bob", "Martin"] as Set
println mySet.getClass().name // java.util.HashSet
mySet.add "Bob"
mySet += "Homer"
println mySet // [Martin, Bob, Homer]
Mis à part le comportement, la seule différence qu'il y a entre un Set et une List est l'on doit écrire as Set lors de sa déclaration.
- La Map :
def emptyMap=[:]
def myMap = [
key1 : "value1",
"key 2": "value2"
]
println myMap.getClass().name //java.util.LinkedHashMap
myMap.put("key3", "value3")
println myMap.get("key 2") // value2
println myMap.key1 // value1
println myMap.values(); // [value1, value2]
Pour définir une Map vide, on va insérer deux points entre les crochets.
Pour l'initialiser avec des couples clé/valeur, on sépare les clés des valeurs par ":" puis chaque couples par des virgules.
Lorsqu'une clé n'a pas d'espaces, celle-ci peut être déclarée sans guillemets.
On peut constater dans l'exemple précédent que la récupération des valeurs est plus aisée.
Il existe aussi un nouveau type de collection qui est présent pour simplifier le développement.
- Le Range :
def numRange = 0..9
println numRange.getClass().name // groovy.lang.IntRange
println numRange // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for (i in 5..0){
print i+", " // 5, 4, 3, 2, 1,
}
println ""
('z'..'a').each{
print it+", " // z, y, x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a,
}
println ""
Les Range sont des séries d'objet.
En séparant le premier objet de la série et le dernier objet par deux points on obtient une collection d'objet se trouvant entre ces deux éléments.
Cela peut fonctionner avec n'importe quel type d'objet tant qu'ils implémentent l'interface Comparable.
Les Opérateurs
- La surcharge des opérateurs
Une des particularités du langage est que l’on peut surcharger les opérateurs. C’est pour cette raison que l’on peut ajouter un élément dans une liste avec le leftShift.
Voici un tableau non exhaustif des méthodes à redéfinir pour changer le comportement d'un opérateur :a + b
a.plus(b)
a * b
a.multiply(b)
a << b
a.leftShift(b)
a & b
a.and(b)
a++
a.next()
a[b]
a.getAt(b)
a[b]=c
a.putAt(b,c)
Je pense qu’il est très dangereux de laisser la possibilité aux développeurs de modifier les opérateurs.
Ce choix est normal. C’est toujours dans la logique de rendre le langage plus flexible.
- De nouveaux opérateurs
Il existe 4 nouveaux opérateurs :
- Le spread operator "*." :
def xpose = [
new HelloGroovy("Etienne", "Duris"),
new HelloGroovy("Dominique", "Revuz")
]
xpose*.sayHello();
//Hello Etienne Duris !
//Hello Dominique Revuz !
Cet opérateur permet d'appliquer une méthode à chaque élément d'une collection.
- L'elvis operator "?:" :
HelloGroovy somebody = new HelloGroovy(null, null)
println((somebody.firstname !=null) ? somebody.firstname : "unknown")
println(somebody.firstname ?: "unknown") // unknown
L’elvis operator permet de renvoyer une valeur par défaut dans le cas ou la valeur testé est nulle.
Ça peut être très pratique dans un formulaire d’une page Web.
- Le safe navigation operator "?." :
somebody=null;
println "Hello ${somebody?.firstname} !"; // Hello null !
//println "Hello ${somebody.firstname} !"; // Throws a NullPointerException
Cet opérateur a pour objectif d’éviter les NullPointerException.
Si on tente d’accéder à une méthode d’un objet nul, la méthode ne sera pas exécuté, et il n’y aura pas d’exception. - Le field operator ".@" :
class Demo {
def field="toto";
def getField(){
"tata"
}
}
def demo = new Demo();
println(demo.field); // tata
println(demo.@field); // toto
Lorsque l’on accède à une valeur d’un champ cela appelle le getter du champs.
Avec le field operator, on peut éviter ça. On accède ainsi directement à la valeur du champ.
- Le spread operator "*." :
Les expressions régulières.
Et oui, ce n'est pas fini avec les nouveaux opérateurs ! Il existe d'autres opérateurs qui sont destinés aux expressions régulières.Il faut toujours garder à l'esprit que ces opérateurs ne sont que des "raccourcis" par rapport aux classes Java déjà existantes qui permettent de manipuler les expressions régulières.
- Le Match operator "==~" :
Cet opérateur retourne une valeur booléenne si le pattern est reconnu.
println(/I:\love\blue\screen/ ==~ /^[A-Z]:(\\[a-zA-Z]+)+$/); // true
println('/Mac/is/fabulous' ==~ /^[A-Z]:(\\[a-zA-Z]+)+$/); //false
- Le Find operator "=~" :
Cet opérateur retourne un tableau avec les groupes du pattern reconnu.
def results = 'hummm.humm@gmail.com' =~ /^([\w._]+)@([\w.]+)\.(fr|ca|net|com|org|info)$/;
if (results){ // if(results!=null)
println results.getClass().name; // java.util.regex.Matcher
results[0].eachWithIndex { it, i ->
println "${i} : ${it}";
}
/*
0 : hummm.humm@gmail.com
1 : hummm.humm
2 : gmail
3 : com
*/
}
- Le Pattern operator "~string" :
Le problème des deux dernier opérateurs c'est que le pattern est recompilé a chaque utilisation.
C’est pour cette raison qu’existe le «pattern operator». Il permet de compiler l’expression régulière, et renvoie un objet Pattern.
Il s’utilise de la même façon qu’en Java.def emails = [
'hummm.humm@gmail.com',
'hummm@progiweb.com',
'mlebas%01@etudiant.univ-mlv.fr'
]
def emailPattern = ~/^([\w._]+)@([\w.]+)\.(fr|ca|net|com|org|info)$/
emails.each{
results=emailPattern.matcher(it);
if (results){
def r=results[0];
println "${it} \t Username : ${r[1]} \t Provider : ${r[2]}"
}else{
println it+" is not a valid e-mail address !"
}
}
/*
hummm.humm@gmail.com Username : hummm.humm Provider : gmail
hummm@progiweb.com Username : hummm Provider : progiweb
mlebas%01@etudiant.univ-mlv.fr is not a valid e-mail address !
*/