Points Clés
- Groovy 3 essaie de combler certaines des lacunes de fonctionnalités qui sont proposées avec les versions récentes de Java
- Les nouvelles fonctionnalités de flux de contrôle incluent l'instruction do-while et la boucle for améliorée
- Des expressions Lambda de style Java ont été ajoutées pour accompagner les closures Groovy
- La gestion automatique des ressources (try-with-resources) arrive également
- L'ensemble d'opérateurs de Groovy a été amélioré avec quelques omissions corrigées (comme ? dans l'indexation des collections)
Apache Groovy est un langage open source orienté objet qui s'exécute sur la machine virtuelle Java (JVM). Groovy est compatible avec la syntaxe Java et à certains égards plus puissant que Java car il est à la fois typé dynamiquement et statiquement (en utilisant le mot-clé def). Groovy est à la fois un langage de programmation et un langage de script. Les fonctionnalités Groovy qui ne sont pas en Java incluent la prise en charge des langages spécifiques au domaine (DSL) et la méta-programmation.
Le Problème
Même si Groovy 2.x est compatible avec la syntaxe Java et se compile en bytecode JVM, il ne prend pas en charge plusieurs des fonctionnalités de Java. Le code Java n'est pas entièrement intégrable directement dans le code Groovy 2.x. Par exemple, les références de méthode et les expressions lambda ne sont pas prises en charge et devraient être converties en closures Groovy.
La solution
Groovy 3 ajoute plusieurs nouvelles fonctionnalités issues de Java, ce qui rend Groovy plus intégrable et interopérable avec Java.
Les nouvelles fonctionnalités de Groovy 3 qui sont influencées par le style de Java incluent la boucle de contrôle de flux do-while, la boucle for améliorée avec prise en charge des expressions séparées par des virgules et des instructions à affectations multiples, initialisation de tableau à l'aide d'accolades {}, les expressions lambda, les références de méthodes, l'instruction try-with-resources, les blocs de code anonymes, l'instanciation de classe interne non statique et l'implémentation par défaut de méthodes d'interfaces. Dans cet article, nous discuterons de ces fonctionnalités et d'autres nouvelles dans Groovy 3.0.0.
La configuration de l'environnement
Téléchargez et installez les logiciels suivants :
- Groovy 3.0.0
- JDK 8 (version minimale prise en charge)
L'instruction de flux de contrôle do-while
L'instruction do-while en Java évalue les instructions dans un bloc do alors qu'une expression est true et a la syntaxe suivante.
do {
statement/s
} while (an_expression);
Les instructions dans le bloc do sont exécutées au moins une fois et après chaque exécution, l'expression an_expression est évaluée.
Groovy 3.0.0 a ajouté la prise en charge de l'instruction do-while. Un exemple d'utilisation de l'instruction do-while dans Groovy est comme suit dans lequel la valeur de x est affichée et incrémentée à l'aide d'un opérateur d'incrémentation postfix dans le bloc do. L'expression dans while évalue si x est inférieur ou égal à 10.
Créez un script Groovy (hello.groovy) et copiez le code suivant dans le script.
def x = 5;
do{
println("x is " + x);
x++;
} while (x <= 10);
Exécutez le script avec la commande groovy hello.groovy et la même résultat qu'avec Java est affichée comme suit :
x is 5
x is 6
x is 7
x is 8
x is 9
x is 10
La boucle for améliorée
L'instruction for existe déjà dans Groovy 2.x mais elle ne prend pas en charge exactement la même syntaxe qu'en Java. Pour résoudre ce problème, Groovy 3.0.0 a ajouté la prise en charge de plusieurs expressions séparées par des virgules, dans les expressions d'initialisation et d'incrémentation dans une instruction for. Par exemple :
for(int i = 1,j = 5; i < 6; i++,j--){
println("i is: " + i);
println("j is: " + j);
}
Exécutez le script Groovy précédent et le même résultat qu'en Java est généré.
i is: 1
j is: 5
i is: 2
j is: 4
i is: 3
j is: 3
i is: 4
j is: 2
i is: 5
j is: 1
La multi-affectation est prise en charge dans Groovy depuis la version 1.6. Groovy 3.0.0 étend cette prise en charge et a ajouté des instructions à affectations multiples dans une boucle for comme dans le script suivant :
def xy = []
for (def (String x, int y) = ['groovw', 1]; y < 4; x++, y++) {
xy << "$x $y"
}
println xy
Si nous exécutons le script précédent, le résultat suivant est affiché.
[groovw 1, groovx 2, groovy 3]
L'initialisation de tableau avec {}
Groovy a omis la prise en charge des accolades de style Java {} dans l'initialisation du tableau car cela pourrait entraîner une confusion avec la syntaxe des closures, qui utilise également des accolades.
Mais, la syntaxe des closures est vraiment différente de la syntaxe d'initialisation du tableau dans laquelle les accolades suivent une déclaration de type tableau. Considérez l'expression d'initialisation de tableau suivante à partir de Java.
String[] strArray=new String[] {"A","B","C"};
Groovy 3.0.0 a ajouté la prise en charge de la syntaxe d'initialisation de tableau dans laquelle les accolades suivent une déclaration de type de tableau comme illustré avec l'exemple d'initialisation de tableau suivant.
def strArray=new String[] {"A","B","C"}
println strArray.size()
println strArray[0]
Exécutez le script Groovy précédent pour générer le résultat suivant :
3
A
Les expressions Lambda
Java a ajouté la prise en charge des expressions lambda dans JDK 8 comme alternative aux classes anonymes pour implémenter des interfaces qui déclarent une seule méthode telle que l'interface java.lang.Runnable, qui déclare une seule méthode run(). La syntaxe d'une expression lambda se compose d'une liste d'arguments suivie d'une flèche suivie d'un bloc ou d'une expression, comme par exemple :
Runnable r2 = () -> System.out.println("Hello from Lambda Expression");
Ou :
Runnable r2 = () -> {}
La méthode run() peut être invoquée comme suit :
r2.run();
Dans Groovy 2.x, les expressions lambda de style Java sont converties en closures. Les closures n'ont pas le même niveau d'inférence de type et de vérification de type et, par conséquent, le même niveau de performance que les expressions lambda offrent. Groovy 3.0.0 a ajouté la prise en charge des expressions lambda. Par exemple, considérons le script Groovy suivant :
def r2 = () -> System.out.println("Hello from Lambda Expression");
r2.run();
Exécutez le script Groovy pour générer le même résultat qu'avec Java comme suit :
Hello from Lambda Expression
Considérez la déclaration suivante dans Groovy qui utilise une closure.
def multiply = { x, y ->
return x*y
}
println multiply(2, 3)
Lorsque le script Groovy est exécuté, le résultat est 6.
Dans Groovy 3.0.0, la closure peut être remplacée par une expression lambda comme dans l'exemple suivant :
def multiply = (int x, int y) -> { return x*y }
println multiply(2, 3)
Si les accolades ne sont pas spécifiées (comme ci-dessous), l'expression après la flèche -> est évaluée et renvoyée d'une expression lambda comme suit :
def multiply = (int x, int y) -> x*y
Les types de paramètres sont facultatifs dans la liste d'arguments comme ci-dessous :
def multiply = (x,y) -> x*y
Si la/les instruction(s) est/sont utilisées après -> les accolades {} sont requises comme ci-dessous :
def multiply = (x,y) -> { z=x*y; println z}
La syntaxe Groovy autorise la valeur par défaut dans la liste des arguments, qui n'a pas d'équivalent en Java, comme ci-dessous :
def multiply = (x,y=1) -> x*y
Comme en Java, aucune parenthèse n'est nécessaire pour un seul paramètre sans type.
def hello = x -> "hello "+x
Les références de méthodes
Les références de méthodes en Java fournissent une syntaxe simplifiée pour les méthodes nommées comme alternative à l'utilisation d'expressions lambda pour créer des méthodes anonymes. Les références de méthodes utilisent l'opérateur ::. Par exemple, considérons la déclaration suivante pour une List paramétrée.
List<String> list = Arrays.asList("Hello", " Deepak", " and"," Hello"," John");
L'instruction suivante utilise une expression lambda pour parcourir la liste et afficher les valeurs des String.
list.forEach((String s)->System.out.println(s));
Le résultat des instructions précédentes est le suivant :
Hello
Deepak
and
Hello
John
Une référence de méthode pourrait être utilisée pour remplacer l'expression lambda avec une syntaxe simplifiée.
list.forEach(System.out::println);
Dans Groovy 2.x, une référence de méthode est convertie en closure. Groovy 3.0.0 ajoute la prise en charge des références de méthode. Le même exemple de référence de méthode peut être exécuté en tant que script Groovy pour générer le même résultat.
Dans l'exemple précédent, une méthode d'instance est invoquée avec une référence de méthode. Comme autre exemple de référence de méthode pour appeler une méthode d'instance, convertissez une liste de valeurs String en majuscules. L'extrait de code Groovy suivant utilise une référence de méthode pour appeler la méthode d'instance toUpperCase dans de la classe String.
import java.util.stream.Collectors;
def list = Arrays.asList("a","b","c","d","e","f","g","h","i","j");
def upperCaseList = list.stream().map(String::toUpperCase).collect(Collectors.toList());
println upperCaseList
Exécutez le script Groovy précédent pour générer le résultat suivant :
[A, B, C, D, E, F, G, H, I, J]
Comme exemple d'utilisation d'une référence de méthode pour appeler une méthode static, considérez le script Groovy suivant dans lequel une liste de valeurs de type String est convertie en une liste d'entiers à l'aide de la méthode static valueOf(String) dans la classe Integer. Un filtre est utilisé pour inclure uniquement les entiers impairs.
import java.util.stream.Collectors;
def list = Arrays.asList("1","2","3","4","5","6","7","8","9","10");
def subList = list.stream().map(Integer::valueOf).filter(number -> number % 2 == 1).collect(Collectors.toList());
println subList
Exécutez le script Groovy pour générer des valeurs entières impaires :
[1, 3, 5, 7, 9]
Les blocs de code anonyme
Un bloc de code anonyme en Java est une instruction ou un groupe d'instructions entre accolades {}. Par exemple, ce qui suit est un bloc de code anonyme.
{
int x = 5;
x--;
System.out.println(x);
}
S'il est exécuté dans JShell, le bloc de code affiche 4. Les blocs de code anonyme en Java sont souvent utilisés pour restreindre la portée d'une variable. Par exemple, exécutez l'instruction try-catch suivante après le bloc de code précédent.
try {
x++;
} catch(Exception ex) {
System.out.println(ex.getMessage());
}
Un message d'erreur est généré indiquant que x n'est pas défini.
cannot find symbol
| symbol: variable x
| x++;
| ^
Groovy 3.0.0 a ajouté la prise en charge des blocs de code anonymes. Le script Groovy suivant définit un bloc de code anonyme qui génère 4.
{
def x = 5
x--
println(x)
}
La portée de x est uniquement dans le bloc de code. Pour le démontrer, exécutez l'instruction try-catch suivante après le bloc de code précédent.
try {
x++
} catch(Exception ex) {
println ex.message
}
Un message d'erreur sur la sortie indique que x n'est pas défini.
No such property: x for class: hello
Un bloc de code anonyme imbriqué définit une autre étendue comme dans le script Groovy suivant qui déclare deux variables appelées "a", chacune dans un bloc différent.
{
{
def a = "Groovy"
def size= a.length()
println size
}
int a = 1
println a
}
Lorsque le script est exécuté, la valeur de "a" des deux blocs est affichée.
6
1
Si un bloc de code est défini après un appel de méthode dans Groovy, il est considéré comme passant une closure comme dernier paramètre de l'appel de méthode. Par exemple, définissez une méthode qui définit un paramètre vararg de type Object et renvoie le nombre d'arguments passés à la méthode. Appelez la méthode et définissez ensuite un bloc de code anonyme.
def hello(Object... args) {println args.length }
hello(1,2)
{5}
On peut s'attendre à ce que la sortie du script Groovy précédent soit 2, mais est en fait 3. Pour ne pas passer le bloc de code anonyme en tant que dernier paramètre à l'appel de méthode, ajoutez un ";" après l'appel de méthode comme ci-dessous :
def hello(Object... args) {println args.length }
hello(1,2);
{5}
L'instanciation de classe interne non statique
Java prend en charge l'instanciation de classe interne non statique à l'aide de l'opérateur new sur une instance de la classe externe. Par exemple, déclarez une classe Hello et déclarez une classe interne Groovy.
public class Hello{
public class Groovy{
int x;
public Groovy(int x){
this.x=x;
}
}
}
Ensuite, créez une instance de la classe externe Hello et instanciez la classe interne comme suit.
int y=new Hello().new Groovy(5).x;
Affichez la valeur de y.
System.out.println(y);
Exécutez l'exemple d'instanciation de classe interne précédent dans JShell et la valeur 5 est affichée.
Groovy 3.0.0 a ajouté la prise en charge de l'instanciation de classe interne non statique. Par exemple, créez un script Groovy qui déclare une classe interne Hello2 dans une classe externe Hello. La classe interne déclare une méthode d'instance qui génère un message "Hello Groovy". La classe externe déclare une méthode static qui prend une instance de la classe externe comme paramètre et renvoie une instance de la classe interne. L'instanciation de classe interne est démontrée à l'aide de l'opérateur new. La méthode main appelle la méthode static dans la classe externe avec une instance de la classe externe et la méthode de classe interne est invoquée sur l'instance de classe interne retournée. Le script Groovy (hello.groovy) est le suivant :
public class Hello {
public class Hello2 {
public void hello() {
println "Hello Groovy"
}
}
public static Hello2 createHello2(Hello y) {
return y.new Hello2();
}
static void main(String... args) {
Hello.createHello2(new Hello()).hello()
}
}
Exécutez le script Groovy script pour avoir le résultat suivant.
Hello Groovy
L'implémentation des méthodes par défaut dans les interfaces
L'implémentation de méthodes par défaut dans les interfaces a été ajoutée dans JDK 8 pour rendre possible l'ajout de nouvelles fonctionnalités à une interface sans rompre la compatibilité binaire avec les anciennes versions de l'interface. Sans implémentation de méthodes par défaut dans une interface, si de nouvelles fonctionnalités sont ajoutées à une interface, cela briserait la compatibilité binaire avec les classes qui implémentent une ancienne version de l'interface. Groovy 2.x implémente les méthodes d'interface par défaut en tant que traits. Groovy 3.0.0 ajoute un support expérimental pour l'implémentation de méthodes par défaut dans les interfaces. Par exemple, déclarez une interface qui inclut une implémentation par défaut pour une méthode et déclarez une classe HelloImpl qui implémente l'interface. Copiez le code suivant dans un script Groovy (hello.groovy).
class HelloImpl implements Hello {
static void main(String... args) {
def hello=new HelloImpl()
hello.hello("Deepak")
}
}
interface Hello {
default void hello(String name){
println "Hello " +name
}
}
Exécutez le script Groovy pour obtenir le résultat suivant.
Hello Deepak
L'instruction try-with-resources
Java prend en charge l'instruction try-with-resources en tant que variante de l'instruction try pour déclarer une ou des ressources avec une ressource étant un objet qui doit être fermé après l'exécution du bloc try. Un exemple de ressource serait un objet Statement de JDBC. Groovy 3.0 a ajouté la prise en charge de try-with-resouces. Par exemple, le script Groovy suivant utilise une instruction try-with-resources pour déclarer une ressource de type Statement.
import java.sql.*
public class JDBC {
static void main(String[] args) {
try {
def connection
= DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql?" + "user=root&password=mysql")
query(connection)
} catch (SQLException e) {
}
}
public static void query(Connection connection) throws SQLException {
def query = "select * from Catalog"
try (def stmt = connection.createStatement()) {
def rs = stmt.executeQuery(query)
while (rs.next()) {
}
} catch (SQLException e) {
println e.getMessage()
}
}
}
Les opérateurs !in et !instanceof
Pour utiliser la forme négative dans Groovy 2.x, les expressions qui contiennent les opérateurs instanceof et in doivent être entre accolades. Par exemple :
def x=5;
assert !(x instanceof List)
assert !(x in [1,2,3,4,5,6])
Groovy 3.0.0 a ajouté la prise en charge des opérateurs !instanceof et !in avec lesquels les accolades ne sont pas nécessaires. Le script précédent pourrait être utilisé comme suit.
def x=5;
assert x !instanceof List
assert x !in [1,2,3,4,5,6]
Les deux scripts génèrent la même sortie :
Caught: Assertion failed:
assert x !in [1,2,3,4,5,6]
| |
5 false
Assertion failed:
assert x !in [1,2,3,4,5,6]
| |
5 false
at Hello.run(Hello.groovy:3)
Les opérateurs de comparaison d'identité
Groovy 3.0.0 ajoute un nouvel opérateur d'identité === qui est identique à la méthode is(). Le script suivant illustre l'utilisation de === avec l'appel de méthode is() équivalent également utilisé.
def a = ['A','B','C']
def b=a
assert a === b
assert a.is(b)
Pour la forme négative équivalente de is(), un nouvel opérateur !== est ajouté. Le script Groovy suivant illustre l'opérateur !== avec le négatif de is() également utilisé.
def c= ['A','B','C']
assert a !== c
assert !a.is(c)
Nouvel analyseur et forme abrégée pour l'opérateur d'affectation
Groovy 3.0.0 a ajouté un nouvel analyseur, appelé analyseur Parrot, qui est plus flexible et plus facile à gérer que l'ancien analyseur et avec la prise en charge d'options et de fonctionnalités de syntaxe supplémentaires. Le nouvel analyseur est activé par défaut.
Groovy 2.x prend déjà en charge une forme abrégée de l'opérateur ternaire. Un opérateur ternaire renvoie une valeur par défaut si une expression renvoie false. Groovy 3.0.0 ajoute une forme abrégé pour l'opérateur d'affectation. Par exemple, dans la variable name de script Groovy suivante, une valeur par défaut est affectée.
import groovy.transform.ToString
@ToString
class Hello {
String name
}
def hello = new Hello(name: 'Deepak')
hello.with {
name ?= 'John'
}
assert hello.toString() == 'Hello(Deepak)'
L'indexation sûre
Les collections Groovy telles que les tableaux, les listes et les maps sont accessibles à l'aide d'un index. L'indexation sécurisée fait référence à l'accès à un index de collection pour obtenir/définir une valeur avec l'opérateur de déréférencement sécurisé ?. Par exemple, déclarez un tableau null.
def anArray = null
La déclaration suivante qui utilise l'opérateur ? fonctionne correctement car null est renvoyé pour toutes les valeurs d'index.
assert null==anArray?[0]
Si l'opérateur ? n'avait pas été utilisé, le message d'erreur suivant serait généré.
Caught: java.lang.NullPointerException: Cannot invoke method getAt() on null object
La déclaration suivante qui utilise l'opérateur ? pour accèder à l'index d'un objet null afin d'affecter une valeur et est ignorée au lieu de générer un message d'erreur.
anArray?[0]='a'//ignores
De même, l'instruction suivante s'exécute correctement car null est renvoyé pour toutes les valeurs d'index.
assert null==anArray?['a']
Et la déclaration suivante qui utilise l'opérateur ? permettant d'accéder à l'index d'un objet null pour définir une valeur est ignoré sans générer de message d'erreur.
anArray?['a']='a'//ignored
Une autre nouvelle fonctionnalité de Groovy 3.0.0 est la possiblité d'utiliser des commentaires Groovydoc intégrables.
Résumé
Dans cet article, nous avons discuté des nouvelles fonctionnalités de Groovy 3.0.0. La plupart des nouvelles fonctionnalités sont extraites de Java, notamment la prise en charge de l'instruction do-while, le style Java pour l'instruction for avec plusieurs expressions dans les expressions d'initialisation et d'incrémentation, les expressions lambda, les références de méthode, les blocs de code anonymes, l'instanciation de classe interne non statique, les méthodes par défaut dans les interfaces et instruction try-with-resources. Les nouvelles fonctionnalités spécifiques à Groovy incluent de nouveaux opérateurs et une indexation sûre.
A propos de l'auteur
Deepak Vohra est Sun Certified Java Programmer et Sun Certified Web Component Developer. Il a publié des articles techniques liés à Java et Java EE dans WebLogic Developer's Journal, XML Journal, ONJava, java.net, IBM developerWorks, Java Developer's Journal, Oracle Magazine et devx. Il a publié cinq livres sur Docker et est Docker Mentor.