Groovy: какая цель "def" в "def x = 0"?

В следующем фрагменте кода (взятом со страницы Руководства по семантике Groovy) зачем добавлять префикс к ключевому слову def?

def x = 0
def y = 5

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

assert x == 5

Ключевое слово def можно удалить, и этот фрагмент даст те же результаты. Так, каков эффект ключевого слова def?

Ответ 1

Синтаксический сахар для базовых скриптов. Опускание ключевого слова "def" помещает переменную в привязки для текущих script и groovy рассматривает ее (в основном) как переменную с областью видимости:

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

Использование ключевого слова def вместо этого не помещает переменную в привязки скриптов:

def y = 2

assert y == 2

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

Отпечатки: "ошибка поймана"

Использование ключевого слова def в больших программах важно, поскольку оно помогает определить область, в которой эта переменная может быть найдена, и может помочь сохранить инкапсуляцию.

Если вы определяете метод в script, он не будет иметь доступа к переменным, которые создаются с помощью "def" в теле основного script, поскольку они не входят в область видимости:

 x = 1
 def y = 2


public bar() {
    assert x == 1

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

bar()

печатает "ошибка поймана"

Переменная "y" не входит в объем внутри функции. "x" находится в области видимости, поскольку groovy проверяет привязки текущего script для переменной. Как я сказал ранее, это просто синтаксический сахар, чтобы быстрее и грязнее писать скрипты (часто один лайнер).

Хорошая практика в больших сценариях - всегда использовать ключевое слово "def" , чтобы вы не сталкивались с проблемами странного охвата или не вмешивались в переменные, которые вы не планируете использовать.

Ответ 2

Тед-ответ отлично подходит для скриптов; Ben answer является стандартным для классов.

Как говорит Бен, подумайте об этом как о "объекте", но он намного круче, поскольку он не ограничивает вас объектными методами. Это имеет неочевидные последствия в отношении импорта.

например. В этом фрагменте я должен импортировать 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()

например. Но здесь я могу просто "крыло", если все находится на пути к классу

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

Ответ 3

В соответствии с этой страница, def заменяет имя типа и может просто считаться псевдонимом для Object (т.е. означает, что вы не заботитесь о типе).

Ответ 4

Что касается этого одиночного script, то практической разницы нет.

Однако переменные, определенные с помощью ключевого слова "def" , рассматриваются как локальные переменные, то есть локальные для этого script. Переменные без "def" перед ними сохраняются в так называемом привязке при первом использовании. Вы можете подумать о привязке как общей области хранения для переменных и закрытий, которые должны быть доступны "между" сценариями.

Итак, если у вас есть два сценария и их исполнение с тем же GroovyShell, второй script сможет получить все переменные, которые были установлены в первом script без "def" .

Ответ 5

Причина для "def" заключается в том, чтобы сообщить groovy, что вы намереваетесь создать переменную здесь. Это важно, потому что вы никогда не захотите создать переменную случайно.

Это несколько приемлемо в скриптах (скрипты Groovy и groovysh позволяют вам это делать), но в рабочем коде это одно из самых больших зол, с которым вы можете столкнуться, поэтому вы должны определить переменную с def во всем реальном коде groovy (что-либо внутри класс).

Вот пример того, почему это плохо. Это запустится (без провала утверждения), если вы скопируете следующий код и вставите его в groovysh:

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

Эта проблема может занять много времени, чтобы найти и исправить. Даже если она всего один раз в жизни, она все равно будет стоить больше времени, чем явное объявление переменных тысяч раз в течение вашей карьеры. Также становится ясно, где именно это было заявлено, не нужно догадываться.

В неважных сценариях/вводе с консоли (например, в groovy console) это несколько приемлемо, поскольку область действия сценария ограничена. Я думаю, единственная причина, по которой groovy позволяет вам делать это в сценариях, - это поддерживать DSL так, как это делает Ruby (плохой компромисс, если вы спросите меня, но некоторые люди любят DSL)

Ответ 6

Собственно, я не думаю, что он будет вести себя одинаково...

переменные в Groovy все еще требуют объявления, а не декларации TYPED, поскольку правая часть обычно содержит достаточно информации для Groovy для ввода переменной.

Когда я пытаюсь использовать переменную, которую я не объявлял с def или типом, я получаю сообщение об ошибке "Нет такого свойства", поскольку он предполагает, что я использую член класса, содержащего код.