Chapitre Les bases du language Groovy
Groovy pour les développeurs Java
Cette leçon présente les principales différences avec Java et Groovy
Groovy pour les développeurs Java
Apprendre Groovy quand on est développeur Java
Groovy essayes d'être aussi naturel que possible pour les développeurs Java.
Dans la mesure du possible, la syntaxe Java est autorisée.
Imports par défaut
Tous ces imports sont importés par défaut et ne nécessitent pas d'être déclarés :
* java.io.*
* java.lang.*
* java.math.BigDecimal
* java.math.BigInteger
* java.net.*
* java.util.*
* groovy.lang.*
* groovy.util.*
Surcharge des méthodes
La surcharge n'est pas résolue statiquement mais dynamiquement en Groovy.
Ainsi l'appel de méthodes est résolu en fonction de la valeur passée à l'exécution du programme.
Création des tableaux
Contrairement à Java, Groovy avant la version 3.0 ne supportait pas l'opérateur d'initialisation de tableaux.
// JAVA
int[] array = {1, 2, 3}; // Java array initializer shorthand syntax
int[] array2 = new int[] {4, 5, 6}; // Longue syntaxe
// GROOVY < 3.0
int[] array = [1, 2, 3]
// GROOVY 3.0+
int[] array = [1, 2, 3]
def array2 = new int[] {1, 2, 3} // Groovy 3.0+ supporte la syntaxe Java
Visibilité des champs
La syntaxe suivante crée une propriété et non seulement un attribut. Une propriété est un attribut accompagné de son getter et de son setter.
class Person {
String name
}
try..resources
Les blocs ARM permettant de libérer les ressources en Java 1.7+ ne sont pas supportés avant Groovy 3.0
Toutefois de nombreuses méthodes utilisant les closures permettent de s'abstraire de leur utilisation.
new File('/path/to/file').withReader('UTF-8') { reader ->
reader.eachLine {
println it
}
}
Lambdas
Les lambdas ne sont supportés qu'avec Groovy 3.0+.
Chaînes de caractères
Groovy offre de nombreux autres syntaxes pour déclarer des chaînes de caractères.
Attention les chaînes de caractères déclarées par des guillemets sont des GString
et non des java.lang.String
. L'utilisation de caractères dollar dans ces chaînes de caractères à un comportement différent.
Types primitifs et wrappers
Groovy utilise des objets pour tout. Donc les types primitifs sont boxés automatiquement.
Opérateur ==
Groovy remplace le comportement de l'opérateur ==. Cet opérateur appelle désormais la méthode equals
.
Multi-méthodes
En Groovy, les méthodes qui doivent être appelées sont choisies à l'exécution. Cette technique d'appel est nommée "multi-méthode". La méthode sera choisie sur la base des types des arguments fournis lors de l'appel. En Java, c'est l'opposé puisqu'une méthode est choisie selon le type de la variable déclarée.
int method(String arg) {
return 1;
}
int method(Object arg) {
return 2;
}
Object o = "Object";
int result = method(o);
En Java, nous aurions eu:
assertEquals(2, result);
Alors qu'en Groovy:
assertEquals(1, result);
Initialisation de tableaux
En Java, les initialisations de tableaux prennent les formes suivantes:
int[] array = {1, 2, 3}; // Java array initializer syntaxe courte
int[] array2 = new int[] {4, 5, 6}; // Java array initializer syntaxe longue
En Groovy, les blocs { … }
sont réservés aux closures donc la syntaxe précédente n'est pas possible. Il faut utiliser l'écriture simplifiée de Groovy à la place :
int[] array = [1, 2, 3]
Pour Groovy 3+, la syntaxe Java a été ré-introduite.
def array2 = new int[] {1, 2, 3}
Visibilité "package"
En Groovy, en enlevant les modifiers de visibilité, le comportement est différent qu'en Java:
class Person {
String name
}
A la place, une propriété va être créée. that is to say a private field, an associated getter and an associated setter.
Il est possible de créer un champ package-private
en l'annotant avec @PackageScope
:
class Person {
@PackageScope String name
}
Blocs ARM
Java 7 a introduit les blocs ARM (Automatic Resource Management) :
Path file = Paths.get("/path/to/file");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
De tels blocs sont désormais supportées en Groovy 3+.
Cependant Groovy fournit des méthodes plus simples pour lire des resources à base de closures.
new File('/path/to/file').eachLine('UTF-8') {
println it
}
Une autre variante assez proche de Java :
new File('/path/to/file').withReader('UTF-8') { reader ->
reader.eachLine {
println it
}
}
Les classes internes
L'implémentation de classes anonymes à l'intérieur de classes est proche de Java avec quelques différences, les variables locales n'ont pas besoin d'être finales.
Les classes internes statiques
Voici un exemple de classe interne statique :
class A {
static class B {}
}
new A.B()
Les classses anonymes internes
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
CountDownLatch called = new CountDownLatch(1)
Timer timer = new Timer()
timer.schedule(new TimerTask() {
void run() {
called.countDown()
}
}, 0)
assert called.await(10, TimeUnit.SECONDS)
Création d'instances de classes non statiques internes
En Java, vous pouvez faire :
public class Y {
public class X {}
public X foo() {
return new X();
}
public static X createX(Y y) {
return y.new X();
}
}
Avant Groovy 3.0.0, ce n'était pas supporté, la seule solution était :
public class Y {
public class X {}
public X foo() {
return new X()
}
public static X createX(Y y) {
return new X(y)
}
}
Les expressions Lambda et l'opérateur de référence de méthodes ::
Java 8+ supporte les expressions Lambda et l'opérateur de référence de méthode ::
Runnable run = () -> System.out.println("Run"); // Java
list.forEach(System.out::println);
Groovy 3+ supporte ces expressions. Avant, il était nécessaire d'utiliser les closures :
Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)
GStrings
Toutes les chaines définies par des guillemets sont interprétées comme des valeurs GString. La conversion de GString en String est généralement automatique.
Toutefois, Groovy peut produire des erreurs de compilation si une méthode Java appelée prend en paramètre la valeur Object. La valeur ne sera pas convertie.
String and Character literals
Les constantes avec des apostrophes sont utilisées en Groovy pour définir des String, et celles en guillemets des GString. Toutefois, si aucune valeur interpolée n'est utilisée, Groovy optimise en String.
assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString
Groovy cast automatiquement une String (ou GString) composée d'un unique caractère en char si la variable est de ce type.
Il est recommandé de caster explicitement les variables en char lors des appels de fonctions.
char a='a'
assert Character.digit(a, 16)==10 : 'But Groovy réalise le boxing'
assert Character.digit((char) 'a', 16)==10
try {
assert Character.digit('a', 16)==10
assert false: 'Need explicit cast'
} catch(MissingMethodException e) {
}
Groovy supporte deux styles de conversion de types et il y a des différences subtiles concernant les chaînes de caractères.
// pour une chaîne de caractère avec un unique caractère, le résultat est le même
assert ((char) "c").class==Character
assert ("c" as char).class==Character
// pour les multi char strings , la différence est importante
try {
((char) 'cx') == 'c'
assert false: 'va échouer, non castable'
} catch(GroovyCastException e) {
}
assert ('cx' as char) == 'c'
assert 'cx'.asType(char) == 'c'
Primitives et wrappers
Parce que Groovy utilise des objets pour tout, il converti toutes les références d'objets en primitives. A cause de cela, il ne suit pas le comportement de Java.
int i
m(i)
// Cette méthode est appelée en Java
void m(long l) {
println "in m(long)"
}
// Cette méthode est appelée par Groovy
void m(Integer i) {
println "in m(Integer)"
}
Le comportement de ==
En Java, l'opérateur == signifie l'égalité pour les types primitifs et l'identité pour les objets.
En Groovy, == signifie l'égalité dans tous les cas.
L'opérateur est converti en to a.compareTo(b) == 0
pour les objets Comparable.
Il est sinon converti en a.equals(b)
autrement
Pour vérifier l'identité il faut utiliser la méthode is comme a.is(b)
.
En Groovy 3+, il est possible d'utiliser l'opérateur === ou !==.