Dernière modification : Dec 08 , 2024

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 !==.

Groovy pour les développeurs Java

Created by Sylvain Leroy