Introduction à la langue Groovy

Introduction à la langue Groovy

1. Vue d'ensemble

Groovy is a dynamic, scripting language for the JVM. Il compile en bytecode et se fond parfaitement dans le code et les bibliothèques Java.

Dans cet article, nous allons examiner certaines des fonctionnalités essentielles deGroovy, notamment la syntaxe de base, les structures de contrôle et les collections.

Nous examinerons ensuite certaines des principales caractéristiques qui en font un langage attrayant, notamment la sécurité nulle, la vérité implicite, les opérateurs et les chaînes.

2. Environnement

Si nous voulons utiliser Groovy dans les projets Maven, nous devons ajouter ce qui suit auxpom.xml:


    
        // ...
        
            org.codehaus.gmavenplus
            gmavenplus-plugin
            1.5
       
   


    // ...
    
        org.codehaus.groovy
        groovy-all
        2.4.10
    

Le plugin Maven le plus récent peut être trouvéhere et la dernière version desgroovy-allhere.

3. Caractéristiques de base

Il existe de nombreuses fonctionnalités utiles dans Groovy. Voyons maintenant les éléments de base du langage et en quoi il diffère de Java.

Voyons maintenant les éléments de base du langage et en quoi il diffère de Java.

3.1. Dactylographie dynamique

L'une des fonctionnalités les plus importantes de Groovy est sa prise en charge de la saisie dynamique.

Les définitions de types sont facultatives et les types réels sont déterminés lors de l'exécution. Jetons un coup d'œil à ces deux classes:

class Duck {
    String getName() {
        'Duck'
    }
}
class Cat {
    String getName() {
        'Cat'
    }
}

Ces deux classes définissent la même méthodegetName, mais elle n'est pas définie explicitement dans un contrat.

Maintenant, imaginez que nous ayons une liste d'objets contenant des canards et des chats qui ont la méthodegetName. Avec Groovy, nous pouvons effectuer les tâches suivantes:

Duck duck = new Duck()
Cat cat = new Cat()

def list = [duck, cat]
list.each { obj ->
    println obj.getName()
}

Le code sera compilé et la sortie du code ci-dessus serait:

Duck
Cat

3.2. Conversion implicite de vérité

Comme en JavaScript, Groovy évalue chaque objet en un booléen si nécessaire, par exemple. lors de son utilisation dans une instructionif ou lors de la négation de la valeur:

if("hello") {...}
if(15) {...}
if(someObject) {...}

Il y a quelques règles simples à retenir à propos de cette conversion:

  • TableauxCollections, non vides, les mappes sont évaluées entrue

  • Matcher avec au moins une correspondance s'évalue àtrue

  • Iterators etEnumerations avec d'autres éléments sont forcés àtrue

  • LesStrings,GStrings etCharSequences non vides sont forcés àtrue

  • Les nombres non nuls sont évalués àtrue

  • Les références d'objet non nulles sont forcées àtrue

Si nous voulons personnaliser la conversion de vérité implicite, nous pouvons définir notre méthodeasBoolean().

3.3. Importations

Certains paquets sont importés par défaut et il n’est pas nécessaire de les importer explicitement:

import java.lang.*
import java.util.*
import java.io.*
import java.net.*

import groovy.lang.*
import groovy.util.*

import java.math.BigInteger
import java.math.BigDecimal

4. Transformations AST

Les transformations AST (Abstract Syntax Tree) nous permettent de nous connecter au processus de compilation Groovy et de le personnaliser pour répondre à nos besoins. Ceci est fait au moment de la compilation, il n'y a donc aucune perte de performances lors de l'exécution de l'application. Nous pouvons créer nos transformations AST, mais nous pouvons également utiliser les transformations intégrées.

Nous pouvons créer nos transformations ou profiter des transformations intégrées.

Jetons un coup d'œil à quelques annotations à connaître.

4.1. AnnotationTypeChecked

Cette annotation est utilisée pour forcer le compilateur à effectuer une vérification de type stricte pour les morceaux de code annotés. Le mécanisme de vérification de type étant extensible, nous pouvons même fournir une vérification de type encore plus stricte que celle disponible en Java lorsque vous le souhaitez.

Jetons un œil à l'exemple ci-dessous:

class Universe {
    @TypeChecked
    int answer() { "forty two" }
}

Si nous essayons de compiler ce code, nous observerons l'erreur suivante:

[Static type checking] - Cannot return value of type java.lang.String on method returning type int

L'annotation@TypeChecked peut être appliquée aux classes et aux méthodes.

4.2. AnnotationCompileStatic

Cette annotation permet au compilateur d'exécuter des vérifications à la compilation, comme c'est le cas avec le code Java. Après cela, le compilateur effectue une compilation statique, contournant ainsi le protocole de métaobjet Groovy.

Lorsqu'une classe est annotée, toutes les méthodes, propriétés, fichiers, classes internes, etc. de la classe annotée seront vérifiés. Lorsqu'une méthode est annotée, la compilation statique est appliquée uniquement aux éléments (fermetures et classes internes anonymes) enfermés par cette méthode.

5. Propriétés

Dans Groovy, nous pouvons créer des POGO (Plain Old Groovy Objects) qui fonctionnent de la même manière que les POJO en Java, bien qu’ils soient plus compacts cargetters and setters are automatically generated for public properties lors de la compilation. Il est important de se rappeler qu’ils ne seront générés que s’ils ne sont pas déjà définis.

Cela nous donne la possibilité de définir des attributs en tant que champs ouverts tout en conservant la possibilité de remplacer le comportement lors de la définition ou de l'obtention des valeurs.

Considérons cet objet:

class Person {
    String name
    String lastName
}

Etant donné que la portée par défaut des classes, champs et méthodes estpublic –, il s'agit d'une classe publique et les deux champs sont publics.

Le compilateur les convertira en champs privés et ajoutera les méthodesgetName(),setName(),getLastName() etsetLasfName(). Si nous définissons lessetter etgetter pour un champ particulier, le compilateur ne créera pas de méthode publique.

5.1. Notations de raccourci

Groovy offre une notation de raccourci pour obtenir et définir les propriétés. À la place de la manière Java d'appeler des getters et des setters, nous pouvons utiliser une notation d'accès de type champ:

resourceGroup.getResourcePrototype().getName() == SERVER_TYPE_NAME
resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME

resourcePrototype.setName("something")
resourcePrototype.name = "something"

6. Les opérateurs

Jetons maintenant un œil aux nouveaux opérateurs ajoutés en plus de ceux connus de Java standard.

6.1. Déréférencement Null-Safe

Le plus populaire est l'opérateur de déréférencement de sécurité null“?” qui nous permet d'éviter unNullPointerException lors de l'appel d'une méthode ou de l'accès à une propriété d'un objetnull. C'est particulièrement utile dans les appels chaînés où une valeurnull peut se produire à un moment donné de la chaîne.

Par exemple, nous pouvons appeler en toute sécurité:

String name = person?.organization?.parent?.name

Dans l'exemple ci-dessus, si unperson,person.organization ouorganization.parent estnull, alorsnull est renvoyé.

6.2. Elvis Opérateur

L'opérateur Elvis“?: ”nous permet de condenser les expressions ternaires. Ces deux sont équivalents:

String name = person.name ?: defaultName

and

String name = person.name ? person.name : defaultName

Ils attribuent tous les deux la valeur deperson.name à la variable de nom si elle estGroovy true (dans ce cas, pasnull et a une longueur denon-zero).

6.3. Opérateur de vaisseau spatial

L'opérateur de vaisseau spatial“<⇒” est un opérateur relationnel qui fonctionne comme lecompareTo() de Java qui compare deux objets et renvoie -1, 0 ou +1 en fonction des valeurs des deux arguments.

Si l'argument de gauche est supérieur à celui de droite, l'opérateur renvoie 1. Si l'argument gauche est inférieur à la droite, l'opérateur renvoie −1. Si les arguments sont égaux, 0 est renvoyé.

Le plus grand avantage de l'utilisation des opérateurs de comparaison est la gestion fluide denulls de telle sorte quex <⇒ y ne lancera jamais unNullPointerException:

println 5 <=> null

L'exemple ci-dessus imprimera 1 à la suite.

7. Les cordes

Il existe plusieurs façons d’exprimer des littéraux de chaîne. L'approche utilisée en Java (chaînes entre guillemets) est prise en charge, mais il est également autorisé d'utiliser des guillemets simples lorsque cela est préférable.

Les chaînes multi-lignes, parfois appelées heredocs dans d'autres langues, sont également prises en charge, avec des guillemets triples (simples ou doubles).

Les chaînes multi-lignes, parfois appelées heredocs dans d'autres langues, sont également prises en charge, avec des guillemets triples (simples ou doubles).

Les chaînes définies avec des guillemets doubles prennent en charge l'interpolation à l'aide de la syntaxe$\{}:

def name = "Bill Gates"
def greeting = "Hello, ${name}"

En fait, toute expression peut être placée à l'intérieur des$\{}:

def name = "Bill Gates"
def greeting = "Hello, ${name.toUpperCase()}"

Une chaîne avec des guillemets doubles est appelée une GString si elle contient une expression$\{}, sinon, c'est un simple objetString.

Le code ci-dessous sera exécuté sans échec du test:

def a = "hello"
assert a.class.name == 'java.lang.String'

def b = 'hello'
assert b.class.name == 'java.lang.String'

def c = "${b}"
assert c.class.name == 'org.codehaus.groovy.runtime.GStringImpl'

8. Collections et cartes

Voyons comment certaines structures de données de base sont gérées.

8.1. Lists

Voici du code pour ajouter quelques éléments à une nouvelle instance deArrayList en Java:

List list = new ArrayList<>();
list.add("Hello");
list.add("World");

Et voici la même opération dans Groovy:

List list = ['Hello', 'World']

Les listes sont par défaut de typejava.util.ArrayList et peuvent également être déclarées explicitement en appelant le constructeur correspondant.

Il n’existe pas de syntaxe distincte pour unSet, mais nous pouvons utiliser le typecoercion pour cela. Soit utiliser:

Set greeting = ['Hello', 'World']

or:

def greeting = ['Hello', 'World'] as Set

8.2. Map

La syntaxe pour unMap est similaire, bien qu'un peu plus verbeuse, car nous devons être en mesure de spécifier des clés et des valeurs délimitées par des deux-points:

def key = 'Key3'
def aMap = [
    'Key1': 'Value 1',
    Key2: 'Value 2',
    (key): 'Another value'
]

Après cette initialisation, nous obtiendrons un nouveauLinkedHashMap avec les entrées:Key1 → Value1, Key2 → Value 2, Key3 → Another Value.

Nous pouvons accéder aux entrées de la carte de plusieurs manières:

println aMap['Key1']
println aMap[key]
println aMap.Key1

9. Structures de contrôle

9.1. Conditions:if-else

Groovy prend en charge la syntaxe conditionnelleif/else comme prévu:

if (...) {
    // ...
} else if (...) {
    // ...
} else {
    // ...
}

9.2. Conditions:switch-case

L'instructionswitch est rétrocompatible avec le code Java afin que nous puissions passer par des cas partageant le même code pour plusieurs correspondances.

La différence la plus importante est qu'unswitch peut effectuer une correspondance avec plusieurs types de valeurs différents:

def x = 1.23
def result = ""

switch ( x ) {
    case "foo":
        result = "found foo"
        break

    case "bar":
        result += "bar"
        break

    case [4, 5, 6, 'inList']:
        result = "list"
        break

    case 12..30:
        result = "range"
        break

    case Number:
        result = "number"
        break

    case ~/fo*/:
        result = "foo regex"
        break

    case { it < 0 }: // or { x < 0 }
        result = "negative"
        break

    default:
        result = "default"
}

println(result)

L'exemple ci-dessus afficheranumber.

9.3. Boucles:while

Groovy prend en charge les boucleswhile habituelles comme le fait Java:

def x = 0
def y = 5

while ( y-- > 0 ) {
    x++
}

9.4. Boucles:for

Groovy embrasse cette simplicité et encourage fortement les bouclesfor suivant cette structure:

for (variable in iterable) { body }

La bouclefor itère suriterable. Les iterables les plus utilisés sont les plages, les collections, les cartes, les tableaux, les itérateurs et les énumérations. En fait, tout objet peut être un objet itérable.

Les accolades autour du corps sont facultatives si elles ne comportent qu’une déclaration. Vous trouverez ci-dessous des exemples d'itérations sur unrange,list,array,map etstrings:

def x = 0
for ( i in 0..9 ) {
    x += i
}

x = 0
for ( i in [0, 1, 2, 3, 4] ) {
    x += i
}

def array = (0..4).toArray()
x = 0
for ( i in array ) {
    x += i
}

def map = ['abc':1, 'def':2, 'xyz':3]
x = 0
for ( e in map ) {
    x += e.value
}

x = 0
for ( v in map.values() ) {
    x += v
}

def text = "abc"
def list = []
for (c in text) {
    list.add(c)
}

L'itération d'objets fait du sloop Groovyfor-une structure de contrôle sophistiquée. C'est un équivalent valide à l'utilisation de méthodes qui itèrent sur un objet avec des fermetures, comme l'utilisation de la méthodeCollection’s each.

La principale différence est que le corps d'une bouclefor n'est pas une fermeture, cela signifie que ce corps est un bloc:

for (x in 0..9) { println x }

alors que ce corps est une fermeture:

(0..9).each { println it }

Même s’ils se ressemblent, leur construction est très différente.

Une fermeture est un objet à part entière et présente des caractéristiques différentes. Il peut être construit à un autre endroit et passé à la méthodeeach. Cependant, le corps du sloopfor-est directement généré en tant quebytecode à son point d'apparition. Aucune règle de portée spéciale ne s'applique.

10. Gestion des exceptions

La grande différence est que la gestion des exceptions vérifiées n'est pas appliquée.

Pour gérer les exceptions générales, nous pouvons placer le code potentiellement à l'origine d'exceptions dans un bloctry/catch:

try {
    someActionThatWillThrowAnException()
} catch (e)
    // log the error message, and/or handle in some way
}

En ne déclarant pas le type d'exception que nous interceptons, toute exception sera interceptée ici.

11. Fermetures

En termes simples, une fermeture est un bloc anonyme de code exécutable qui peut être transmis à des variables et a accès aux données dans le contexte où il a été défini.

Elles sont également similaires aux classes internes anonymes, bien qu’elles n’implémentent ni une interface, ni une classe de base. Ils ressemblent aux lambdas de Java.

Il est intéressant de noter que Groovy peut tirer pleinement parti des ajouts JDK introduits pour prendre en charge les lambdas, notamment l’API de diffusion en continu. Nous pouvons toujours utiliser des fermetures où des expressions lambda sont attendues.

Prenons l'exemple ci-dessous:

def helloWorld = {
    println "Hello World"
}

La variablehelloWorld contient maintenant une référence à la fermeture, et nous pouvons l'exécuter en appelant sa méthodecall:

helloWorld.call()

Groovy nous permet d'utiliser une syntaxe d'appel de méthode plus naturelle - il invoque la méthodecall pour nous:

helloWorld()

11.1. Paramètres

Comme les méthodes, les fermetures peuvent avoir des paramètres. Il y a trois variantes.

Dans ce dernier exemple, comme il n'y a rien de declpersistence_startared, il n'y a qu'un seul paramètre avec le nom par défautit. La fermeture modifiée qui imprime ce qui est envoyé serait:

def printTheParam = { println it }

Nous pourrions l'appeler comme ceci:

printTheParam('hello')
printTheParam 'hello'

Nous pouvons également nous attendre à des paramètres dans les fermetures et les transmettre lors de l'appel:

def power = { int x, int y ->
    return Math.pow(x, y)
}
println power(2, 3)

La définition de type des paramètres est la même que celle des variables. Si nous définissons un type, nous ne pouvons utiliser que ce type, mais nous pouvons également le transmettre et transmettre tout ce que nous voulons:

def say = { what ->
    println what
}
say "Hello World"

11.2. Retour facultatif

La dernière instruction d'une fermeture peut être retournée implicitement sans qu'il soit nécessaire d'écrire une instruction return. Ceci peut être utilisé pour réduire le code passe-partout au minimum. Ainsi, une fermeture qui calcule le carré d’un nombre peut être raccourcie comme suit:

def square = { it * it }
println square(4)

Cette fermeture utilise le paramètre impliciteit et l'instruction de retour facultative.

12. Conclusion

Cet article a fourni une introduction rapide au langage Groovy et à ses principales fonctionnalités. Nous avons commencé par introduire des concepts simples tels que la syntaxe de base, les instructions conditionnelles et les opérateurs. Nous avons également démontré certaines fonctionnalités plus avancées telles que les opérateurs et les fermetures.

Si vous voulez trouver plus d'informations sur le langage et sa sémantique, vous pouvez aller directement auxofficial site.