Groovy: quel est le but de «def» dans «def x = 0»?

180

Dans le morceau de code suivant (tiré de la page Groovy Semantics Manual ), pourquoi préfixer l'affectation avec le mot def- clé ?

def x = 0
def y = 5

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

assert x == 5

Le defmot-clé peut être supprimé et cet extrait de code produirait les mêmes résultats. Alors, quel est l' effet du mot def- clé ?

Léonel
la source

Réponses:

278

C'est du sucre syntaxique pour les scripts de base. Omettre le mot-clé "def" place la variable dans les liaisons du script actuel et groovy la traite (principalement) comme une variable à portée globale:

x = 1
assert x == 1
assert this.binding.getVariable("x") == 1

L'utilisation du mot-clé def à la place ne place pas la variable dans les liaisons des scripts:

def y = 2

assert y == 2

try {
    this.binding.getVariable("y") 
} catch (groovy.lang.MissingPropertyException e) {
    println "error caught"
} 

Impressions: "erreur interceptée"

L'utilisation du mot-clé def dans des programmes plus volumineux est importante car elle permet de définir la portée dans laquelle la variable peut être trouvée et peut aider à préserver l'encapsulation.

Si vous définissez une méthode dans votre script, elle n'aura pas accès aux variables créées avec "def" dans le corps du script principal car elles ne sont pas dans la portée:

 x = 1
 def y = 2


public bar() {
    assert x == 1

    try {
        assert y == 2
    } catch (groovy.lang.MissingPropertyException e) {
        println "error caught"
    }
}

bar()

imprime "erreur interceptée"

La variable "y" n'est pas dans la portée de la fonction. "x" est dans la portée car groovy vérifiera les liaisons du script courant pour la variable. Comme je l'ai dit plus tôt, il s'agit simplement de sucre syntaxique pour rendre les scripts rapides et sales plus rapides à taper (souvent un liners).

La bonne pratique dans les scripts plus volumineux est de toujours utiliser le mot clé "def" afin de ne pas rencontrer d'étranges problèmes de portée ou d'interférer avec des variables que vous n'avez pas l'intention de faire.

Ted Naleid
la source
36

La réponse de Ted est excellente pour les scripts; La réponse de Ben est standard pour les cours.

Comme Ben le dit, considérez-le comme "Object" - mais c'est beaucoup plus cool en ce qu'il ne vous contraint pas aux méthodes Object. Cela a des implications nettes en ce qui concerne les importations.

Par exemple, dans cet extrait de code, je dois importer FileChannel

// Groovy imports java.io.* and java.util.* automatically
// but not java.nio.*

import java.nio.channels.*

class Foo {
    public void bar() {
        FileChannel channel = new FileInputStream('Test.groovy').getChannel()
        println channel.toString()
    }
}

new Foo().bar()

par exemple, mais ici, je peux simplement 'l'ailer' tant que tout est sur le chemin des classes

// Groovy imports java.io.* and java.util.* automatically
// but not java.nio.*
class Foo {
    public void bar() {
        def channel = new FileInputStream('Test.groovy').getChannel()
        println channel.toString()
    }
}

new Foo().bar()
Michael Easter
la source
1
pourquoi avez-vous été autorisé à ne new FileInputStream('Test.groovy').getChannel()pas importer?
Alexander Suraphel
3
@AlexanderSuraphel "tant que tout est sur le chemin des classes"
Hanno
30

Selon cette page , defremplace un nom de type et peut simplement être considéré comme un alias pour Object(c'est-à-dire signifiant que vous ne vous souciez pas du type).

Ben Hoffstein
la source
12

En ce qui concerne ce script unique, il n'y a pas de différence pratique.

Cependant, les variables définies à l'aide du mot-clé "def" sont traitées comme des variables locales, c'est-à-dire locales à ce script. Les variables sans "def" devant elles sont stockées dans une soi-disant liaison lors de la première utilisation. Vous pouvez considérer la liaison comme une zone de stockage générale pour les variables et les fermetures qui doivent être disponibles «entre» les scripts.

Donc, si vous avez deux scripts et que vous les exécutez avec le même GroovyShell, le second script pourra récupérer toutes les variables définies dans le premier script sans "def".


la source
8

La raison de "def" est de dire à groovy que vous avez l'intention de créer une variable ici. C'est important car vous ne voulez jamais créer une variable par accident.

C'est un peu acceptable dans les scripts (les scripts Groovy et groovysh vous permettent de le faire), mais dans le code de production, c'est l'un des plus gros maux que vous puissiez rencontrer, c'est pourquoi vous devez définir une variable avec def dans tout le code groovy réel (tout ce qui se trouve à l'intérieur d'un classe).

Voici un exemple de pourquoi c'est mauvais. Cela s'exécutera (sans échouer l'assertion) si vous copiez le code suivant et le collez dans groovysh:

bill = 7
bi1l = bill + 3
assert bill == 7

Ce type de problème peut prendre beaucoup de temps à trouver et à résoudre - Même s'il ne vous a mordu qu'une fois dans votre vie, cela vous coûterait encore plus de temps que de déclarer explicitement les variables des milliers de fois au cours de votre carrière. Il devient également clair à l'œil où il est déclaré, vous n'avez pas à deviner.

Dans les scripts / entrées de console sans importance (comme la console groovy), c'est quelque peu acceptable car la portée du script est limitée. Je pense que la seule raison pour laquelle groovy vous permet de faire cela dans les scripts est de prendre en charge les DSL comme le fait Ruby (un mauvais compromis si vous me le demandez, mais certaines personnes aiment les DSL)

Bill K
la source
5

En fait, je ne pense pas que ça se comporterait de la même manière ...

les variables dans Groovy nécessitent toujours une déclaration, mais pas une déclaration TYPED, car le côté droit contient généralement suffisamment d'informations pour que Groovy puisse saisir la variable.

Lorsque j'essaye d'utiliser une variable que je n'ai pas déclarée avec def ou un type, j'obtiens une erreur "No such property", car elle suppose que j'utilise un membre de la classe contenant le code.

billjamesdev
la source