Groovy: ¿cuál es el propósito de "def" en "def x = 0"?

180

En el siguiente código (tomado de la página Groovy Semantics Manual ), ¿por qué prefijar la asignación con la palabra clave def?

def x = 0
def y = 5

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

assert x == 5

La defpalabra clave se puede eliminar, y este fragmento produciría los mismos resultados. Entonces, ¿cuál es el efecto de la palabra clave def?

Leonel
fuente

Respuestas:

278

Es azúcar sintáctico para guiones básicos. Omitir la palabra clave "def" coloca la variable en los enlaces para el script actual y Groovy la trata (principalmente) como una variable de alcance global:

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

El uso de la palabra clave def en su lugar no coloca la variable en los enlaces de los scripts:

def y = 2

assert y == 2

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

Impresiones: "error detectado"

El uso de la palabra clave def en programas más grandes es importante ya que ayuda a definir el alcance en el que se puede encontrar la variable y puede ayudar a preservar la encapsulación.

Si define un método en su script, no tendrá acceso a las variables que se crean con "def" en el cuerpo del script principal ya que no están dentro del alcance:

 x = 1
 def y = 2


public bar() {
    assert x == 1

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

bar()

imprime "error detectado"

La variable "y" no está dentro del alcance de la función. "x" está dentro del alcance ya que groovy verificará los enlaces del script actual para la variable. Como dije antes, esto es simplemente azúcar sintáctica para hacer que las secuencias de comandos rápidas y sucias sean más rápidas de escribir (a menudo, un revestimiento).

Una buena práctica en los scripts más grandes es usar siempre la palabra clave "def" para no tener problemas de alcance extraños o interferir con variables que no tiene intención.

Ted Naleid
fuente
36

La respuesta de Ted es excelente para los guiones; La respuesta de Ben es estándar para las clases.

Como dice Ben, piense en él como "Objeto", pero es mucho más genial ya que no lo limita a los métodos de Objeto. Esto tiene claras implicaciones con respecto a las importaciones.

Por ejemplo, en este fragmento tengo que importar 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 ejemplo, pero aquí puedo simplemente "volar" siempre y cuando todo esté en el classpath

// 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
fuente
1
¿Por qué se le permitió new FileInputStream('Test.groovy').getChannel()sin la importación?
Alexander Suraphel el
3
@AlexanderSuraphel "siempre y cuando todo esté en el classpath"
Hanno
30

De acuerdo con esta página , defes un reemplazo para un nombre de tipo y simplemente puede considerarse como un alias para Object(es decir, significa que no le importa el tipo).

Ben Hoffstein
fuente
12

En lo que respecta a este script único, no hay diferencia práctica.

Sin embargo, las variables definidas con la palabra clave "def" se tratan como variables locales, es decir, locales para este script. Las variables sin la "def" frente a ellas se almacenan en un llamado enlace desde el primer uso. Puede pensar en el enlace como un área de almacenamiento general para variables y cierres que deben estar disponibles "entre" secuencias de comandos.

Entonces, si tiene dos scripts y los ejecuta con el mismo GroovyShell, el segundo script podrá obtener todas las variables que se establecieron en el primer script sin un "def".


fuente
8

La razón de "def" es decirle a Groovy que tiene la intención de crear una variable aquí. Es importante porque nunca quieres crear una variable por accidente.

Es algo aceptable en las secuencias de comandos (las secuencias de comandos Groovy y groovysh le permiten hacerlo), pero en el código de producción es uno de los males más grandes que puede encontrar, por eso debe definir una variable con def en todo el código maravilloso real (cualquier cosa dentro de un clase).

Aquí hay un ejemplo de por qué es malo. Esto se ejecutará (sin fallar la afirmación) si copia el siguiente código y lo pega en groovysh:

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

Este tipo de problema puede llevar mucho tiempo encontrarlo y solucionarlo: incluso si solo lo mordió una vez en su vida, le costaría más tiempo que declarar explícitamente las variables miles de veces a lo largo de su carrera. También queda claro a simple vista dónde se declara, no tiene que adivinar.

En scripts / entradas de consola sin importancia (como la consola groovy) es algo aceptable porque el alcance del script es limitado. Creo que la única razón por la que Groovy te permite hacer esto en los scripts es para admitir DSL de la misma manera que Ruby (una mala compensación si me preguntas, pero a algunas personas les encantan los DSL)

Bill K
fuente
5

En realidad, no creo que se comporte igual ...

Las variables en Groovy aún requieren declaración, pero no declaración TYPED, ya que el lado derecho generalmente contiene suficiente información para que Groovy escriba la variable.

Cuando intento usar una variable que no he declarado con def o un tipo, aparece el error "No existe esa propiedad", ya que supone que estoy usando un miembro de la clase que contiene el código.

billjamesdev
fuente