Groovy: Was ist der Zweck von "def" in "def x = 0"?

180

Warum sollte der Zuweisung im folgenden Code (entnommen aus der Seite Groovy Semantics Manual ) das Schlüsselwort vorangestellt werden def?

def x = 0
def y = 5

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

assert x == 5

Das defSchlüsselwort kann entfernt werden, und dieses Snippet würde zu denselben Ergebnissen führen. Wie wirkt sich das Keyword aus def?

Leonel
quelle

Antworten:

278

Es ist syntaktischer Zucker für grundlegende Skripte. Wenn Sie das Schlüsselwort "def" weglassen, wird die Variable in die Bindungen für das aktuelle Skript eingefügt, und groovy behandelt sie (meistens) wie eine Variable mit globalem Gültigkeitsbereich:

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

Wenn Sie stattdessen das Schlüsselwort def verwenden, wird die Variable nicht in die Skriptbindungen eingefügt:

def y = 2

assert y == 2

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

Drucke: "Fehler abgefangen"

Die Verwendung des Schlüsselworts def in größeren Programmen ist wichtig, da es dabei hilft, den Bereich zu definieren, in dem die Variable gefunden werden kann, und die Kapselung beizubehalten.

Wenn Sie eine Methode in Ihrem Skript definieren, hat sie keinen Zugriff auf die Variablen, die mit "def" im Hauptteil des Hauptskripts erstellt wurden, da sie nicht im Gültigkeitsbereich liegen:

 x = 1
 def y = 2


public bar() {
    assert x == 1

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

bar()

druckt "Fehler abgefangen"

Die Variable "y" befindet sich nicht im Gültigkeitsbereich der Funktion. "x" ist im Gültigkeitsbereich, da groovy die Bindungen des aktuellen Skripts für die Variable überprüft. Wie ich bereits sagte, ist dies einfach syntaktischer Zucker, um schnelle und schmutzige Skripte schneller zu tippen (oft ein Liner).

In größeren Skripten empfiehlt es sich, immer das Schlüsselwort "def" zu verwenden, damit Sie nicht auf seltsame Probleme mit dem Gültigkeitsbereich stoßen oder Variablen stören, die Sie nicht beabsichtigen.

Ted Naleid
quelle
36

Teds Antwort eignet sich hervorragend für Skripte. Bens Antwort ist Standard für Klassen.

Wie Ben sagt, stellen Sie es sich als "Objekt" vor - aber es ist viel cooler, da es Sie nicht auf die Objektmethoden beschränkt. Dies hat gute Auswirkungen auf die Importe.

zB In diesem Snippet muss ich FileChannel importieren

// 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()

zB Aber hier kann ich es einfach "beflügeln", solange sich alles auf dem Klassenweg befindet

// 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 Ostern
quelle
1
Warum durften Sie new FileInputStream('Test.groovy').getChannel()ohne Import?
Alexander Suraphel
3
@ AlexanderSuraphel "solange alles auf dem Klassenweg ist"
Hanno
30

Nach dieser Seite , defist ein Ersatz für einen Typnamen und kann einfach von zu als Alias gedacht werden Object(dh was bedeutet , dass Sie nicht über die Art egal).

Ben Hoffstein
quelle
12

In Bezug auf dieses einzelne Skript gibt es keinen praktischen Unterschied.

Mit dem Schlüsselwort "def" definierte Variablen werden jedoch als lokale Variablen behandelt, dh lokal für dieses eine Skript. Variablen ohne das "def" vor ihnen werden bei der ersten Verwendung in einer sogenannten Bindung gespeichert. Sie können sich die Bindung als allgemeinen Speicherbereich für Variablen und Abschlüsse vorstellen, die "zwischen" Skripten verfügbar sein müssen.

Wenn Sie also zwei Skripte haben und diese mit derselben GroovyShell ausführen, kann das zweite Skript alle Variablen abrufen, die im ersten Skript ohne "def" festgelegt wurden.


quelle
8

Der Grund für "def" ist, groovy mitzuteilen, dass Sie hier eine Variable erstellen möchten. Dies ist wichtig, da Sie niemals versehentlich eine Variable erstellen möchten.

In Skripten ist dies einigermaßen akzeptabel (Groovy-Skripte und Groovysh ermöglichen dies), aber im Produktionscode ist es eines der größten Übel, auf das Sie stoßen können. Deshalb müssen Sie in jedem tatsächlichen Groovy-Code eine Variable mit def definieren (alles in a Klasse).

Hier ist ein Beispiel, warum es schlecht ist. Dies wird ausgeführt (ohne dass die Bestätigung fehlschlägt), wenn Sie den folgenden Code kopieren und in groovysh einfügen:

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

Diese Art von Problem kann viel Zeit in Anspruch nehmen, um es zu finden und zu beheben. Selbst wenn es Sie nur einmal in Ihrem Leben gebissen hat, würde es mehr Zeit kosten, als die Variablen im Laufe Ihrer Karriere tausende Male explizit zu deklarieren. Es wird dem Auge auch klar, wo es deklariert wird, man muss nicht raten.

In unwichtigen Skripten / Konsoleneingaben (wie der groovigen Konsole) ist dies einigermaßen akzeptabel, da der Umfang des Skripts begrenzt ist. Ich denke, der einzige Grund, warum groovy es Ihnen erlaubt, dies in Skripten zu tun, ist die Unterstützung von DSLs wie Ruby (Ein schlechter Kompromiss, wenn Sie mich fragen, aber einige Leute lieben die DSLs).

Bill K.
quelle
5

Eigentlich glaube ich nicht, dass es sich gleich verhalten würde ...

Variablen in Groovy erfordern weiterhin eine Deklaration, nur keine TYPED-Deklaration, da die rechte Seite im Allgemeinen genügend Informationen enthält, damit Groovy die Variable eingeben kann.

Wenn ich versuche, eine Variable zu verwenden, die ich nicht mit def oder einem Typ deklariert habe, wird die Fehlermeldung "Keine solche Eigenschaft" angezeigt, da davon ausgegangen wird, dass ich ein Mitglied der Klasse verwende, die den Code enthält.

billjamesdev
quelle