Groovy: какова цель «def» в «def x = 0»?

180

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

def x = 0
def y = 5

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

assert x == 5

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

Леонель
источник

Ответы:

278

Это синтаксический сахар для базовых сценариев. Пропуск ключевого слова «def» помещает переменную в привязки для текущего скрипта, а 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 в больших программах важно, поскольку оно помогает определить область, в которой можно найти переменную, и может помочь сохранить инкапсуляцию.

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

 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 проверит привязки текущего скрипта для переменной. Как я уже говорил ранее, это просто синтаксический сахар, позволяющий быстрее и быстрее набирать быстрые и грязные сценарии (часто один вкладыш).

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

Тед Нейлид
источник
36

Ответ Теда отлично подходит для сценариев; Ответ бена стандартный для занятий.

Как говорит Бен, думайте о нем как об «объекте» - но он гораздо круче, поскольку он не ограничивает вас методами объекта. Это имеет хорошие последствия в отношении импорта.

например, в этом фрагменте я должен импортировать 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()
Майкл Пасха
источник
1
почему вам разрешили new FileInputStream('Test.groovy').getChannel()без импорта?
Александр Сурафель
3
@AlexanderSuraphel "пока все на пути к классам"
Hanno
30

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

Бен Хоффштейн
источник
12

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

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

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


источник
8

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

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

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

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

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

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

Билл К
источник
5

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

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

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

billjamesdev
источник