Groovy: jaki jest cel „def” w „def x = 0”?

180

W poniższym kodzie (zaczerpniętym ze strony Groovy Semantics Manual ), po co poprzedzać przypisanie słowem kluczowym def?

def x = 0
def y = 5

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

assert x == 5

Słowo defkluczowe można usunąć, a ten fragment kodu przyniesie takie same wyniki. Jaki jest zatem wpływ słowa kluczowego def?

Leonel
źródło

Odpowiedzi:

278

To cukier syntaktyczny dla podstawowych skryptów. Pominięcie słowa kluczowego „def” umieszcza zmienną w powiązaniach bieżącego skryptu i groovy traktuje ją (głównie) jak zmienną o globalnym zasięgu:

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

Zamiast tego użycie słowa kluczowego def nie powoduje umieszczenia zmiennej w powiązaniach skryptów:

def y = 2

assert y == 2

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

Wydruki: „wykryto błąd”

Użycie słowa kluczowego def w większych programach jest ważne, ponieważ pomaga zdefiniować zakres, w którym można znaleźć zmienną, i może pomóc zachować enkapsulację.

Jeśli zdefiniujesz metodę w skrypcie, nie będzie ona miała dostępu do zmiennych utworzonych za pomocą „def” w treści skryptu głównego, ponieważ nie są one objęte zakresem:

 x = 1
 def y = 2


public bar() {
    assert x == 1

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

bar()

drukuje „złapany błąd”

Zmienna „y” nie wchodzi w zakres funkcji. „x” jest w zakresie, ponieważ groovy sprawdzi powiązania bieżącego skryptu dla zmiennej. Jak powiedziałem wcześniej, jest to po prostu cukier składniowy, dzięki któremu szybkie i brudne skrypty szybciej się pisze (często jedna linijka).

Dobrą praktyką w przypadku większych skryptów jest zawsze używanie słowa kluczowego „def”, aby nie napotkać dziwnych problemów dotyczących zakresu lub ingerować w zmienne, których nie zamierzasz używać.

Ted Naleid
źródło
36

Odpowiedź Teda jest doskonała dla skryptów; Odpowiedź Bena jest standardem na zajęciach.

Jak mówi Ben, pomyśl o tym jak o „obiekcie” - ale jest o wiele fajniejszy, ponieważ nie ogranicza cię do metod obiektowych. Ma to zgrabne konsekwencje w odniesieniu do importu.

np. W tym fragmencie muszę zaimportować 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()

np. Ale tutaj mogę po prostu „uskrzydlić”, o ile wszystko jest na ścieżce klasowej

// 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
źródło
1
dlaczego pozwolono ci new FileInputStream('Test.groovy').getChannel()bez importu?
Alexander Suraphel,
3
@AlexanderSuraphel „tak długo, jak wszystko jest na dobrej drodze”
Hanno
30

Według tej strony , defjest zamiennikiem dla nazwy typu i może być po prostu traktowane jako alias Object(tj oznaczający, że nie dbają o jej rodzaju).

Ben Hoffstein
źródło
12

Jeśli chodzi o ten pojedynczy skrypt, nie ma praktycznej różnicy.

Jednak zmienne zdefiniowane za pomocą słowa kluczowego „def” są traktowane jak zmienne lokalne, to znaczy lokalne dla tego jednego skryptu. Zmienne bez „def” przed nimi są przechowywane w tak zwanym wiązaniu przy pierwszym użyciu. Możesz myśleć o powiązaniu jako o ogólnym obszarze przechowywania zmiennych i zamknięć, które muszą być dostępne między skryptami.

Tak więc, jeśli masz dwa skrypty i wykonujesz je za pomocą tego samego GroovyShell, drugi skrypt będzie mógł uzyskać wszystkie zmienne ustawione w pierwszym skrypcie bez „def”.


źródło
8

Powodem „def” jest powiedzenie groovy'emu, że zamierzasz tutaj utworzyć zmienną. Jest to ważne, ponieważ nigdy nie chcesz tworzyć zmiennej przez przypadek.

Jest to w pewnym stopniu akceptowalne w skryptach (pozwalają na to skrypty Groovy i groovysh), ale w kodzie produkcyjnym jest to jedno z największych zła, z jakim można się spotkać, dlatego należy zdefiniować zmienną z def w całym rzeczywistym kodzie Groovy (wszystko w klasa).

Oto przykład, dlaczego jest zły. To uruchomi się (bez niepowodzenia asercji), jeśli skopiujesz następujący kod i wkleisz go do groovysh:

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

Tego rodzaju problem może zająć dużo czasu, aby go znaleźć i naprawić - nawet jeśli ugryzłby cię tylko raz w życiu, nadal kosztowałby więcej czasu niż jawne deklarowanie zmiennych tysiące razy w całej karierze. Dla oka staje się też jasne, gdzie jest deklarowane, nie musisz zgadywać.

W nieistotnych danych wejściowych skryptów / konsoli (takich jak groovy console) jest to w pewnym stopniu dopuszczalne, ponieważ zakres skryptu jest ograniczony. Myślę, że jedynym powodem, dla którego groovy pozwala ci to zrobić w skryptach, jest obsługa DSL tak, jak robi to Ruby (zły kompromis, jeśli mnie pytasz, ale niektórzy ludzie kochają DSL)

Bill K.
źródło
5

Właściwie nie sądzę, że zachowałby się tak samo ...

Zmienne w Groovy nadal wymagają deklaracji, po prostu nie deklaracji TYPED, ponieważ prawa strona zazwyczaj zawiera wystarczającą ilość informacji, aby Groovy mógł wpisać zmienną.

Kiedy próbuję użyć zmiennej, której nie zadeklarowałem z def lub typem, pojawia się błąd „Brak takiej właściwości”, ponieważ zakłada, że ​​używam elementu klasy zawierającego kod.

billjamesdev
źródło