Groovy: qual é o propósito de "def" em "def x = 0"?

180

No seguinte trecho de código (retirado da página do Groovy Semantics Manual ), por que prefixar a atribuição com a palavra-chave def?

def x = 0
def y = 5

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

assert x == 5

A defpalavra-chave pode ser removida e esse snippet produziria os mesmos resultados. Então, qual é o efeito da palavra-chave def?

Leonel
fonte

Respostas:

278

É um açúcar sintático para scripts básicos. Omitir a palavra-chave "def" coloca a variável nas ligações do script atual e o groovy a trata (principalmente) como uma variável com escopo global:

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

O uso da palavra-chave def não coloca a variável nas ligações dos scripts:

def y = 2

assert y == 2

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

Impressões: "erro detectado"

O uso da palavra-chave def em programas maiores é importante, pois ajuda a definir o escopo no qual a variável pode ser encontrada e a preservar o encapsulamento.

Se você definir um método no seu script, ele não terá acesso às variáveis ​​criadas com "def" no corpo do script principal, pois elas não estão no escopo:

 x = 1
 def y = 2


public bar() {
    assert x == 1

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

bar()

imprime "erro detectado"

A variável "y" não está no escopo dentro da função. "x" está no escopo, já que o groovy verifica as ligações do script atual para a variável. Como eu disse anteriormente, isso é simplesmente açúcar sintático para tornar scripts rápidos e sujos mais rápidos de digitar (geralmente um forro).

A boa prática em scripts maiores é sempre usar a palavra-chave "def" para que você não tenha problemas de escopo estranhos ou interfira nas variáveis ​​que não pretende.

Ted Naleid
fonte
36

A resposta de Ted é excelente para scripts; A resposta de Ben é padrão para as aulas.

Como Ben diz, pense nisso como "Objeto" - mas é muito mais interessante porque não o restringe aos métodos Object. Isso tem implicações claras em relação às importações.

Por exemplo, neste trecho eu tenho que importar o 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()

por exemplo, mas aqui eu posso simplesmente 'voar' desde que tudo esteja no caminho da classe

// 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
fonte
1
por que você teve permissão para new FileInputStream('Test.groovy').getChannel()não importar?
Alexander Suraphel
3
@AlexanderSuraphel "desde que tudo esteja no caminho da classe"
Hanno
30

De acordo com esta página , defé um substituto para um nome de tipo e pode ser simplesmente considerado um alias para Object(isto é, significa que você não se importa com o tipo).

Ben Hoffstein
fonte
12

No que diz respeito a esse script único, não há diferença prática.

No entanto, variáveis ​​definidas usando a palavra-chave "def" são tratadas como variáveis ​​locais, ou seja, locais para esse script. Variáveis ​​sem o "def" na frente delas são armazenadas na chamada ligação no primeiro uso. Você pode pensar na ligação como uma área de armazenamento geral para variáveis ​​e fechamentos que precisam estar disponíveis "entre" scripts.

Portanto, se você tiver dois scripts e executá-los com o mesmo GroovyShell, o segundo script poderá obter todas as variáveis ​​definidas no primeiro script sem um "def".


fonte
8

A razão para "def" é dizer ao groovy que você pretende criar uma variável aqui. É importante porque você nunca deseja criar uma variável por acidente.

É algo aceitável em scripts (scripts Groovy e groovysh permitem que você faça isso), mas no código de produção é um dos maiores males que você pode encontrar, e é por isso que você deve definir uma variável com def em todo o código groovy real (qualquer coisa dentro de um classe).

Aqui está um exemplo de por que é ruim. Isso será executado (sem falhar a afirmação) se você copiar o código a seguir e colá-lo no groovysh:

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

Esse tipo de problema pode levar muito tempo para ser solucionado - mesmo que você tenha mordido apenas uma vez na vida, ainda custaria mais tempo do que declarar explicitamente as variáveis ​​milhares de vezes ao longo de sua carreira. Também fica claro aos olhos exatamente onde está sendo declarado, você não precisa adivinhar.

Na entrada de scripts / console sem importância (como o console do groovy), é algo aceitável porque o escopo do script é limitado. Acho que a única razão pela qual o groovy permite que você faça isso em scripts é oferecer suporte a DSLs como Ruby (uma troca ruim, se você me perguntar, mas algumas pessoas adoram as DSLs)

Bill K
fonte
5

Na verdade, eu não acho que se comportaria da mesma maneira ...

As variáveis ​​no Groovy ainda exigem declaração, mas não a declaração TYPED, pois o lado direito geralmente contém informações suficientes para o Groovy digitar a variável.

Quando tento usar uma variável que não declarei com def ou um tipo, recebo o erro "Nenhuma dessas propriedades", pois assume que estou usando um membro da classe que contém o código.

billjamesdev
fonte