Groovy: яка мета "def" у "def x = 0"?


180

У наступному фрагменті коду (взятому зі сторінки Посібника з Groovy Semantics ), чому приставку присвоювати ключовому слову 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 консолі) це дещо прийнятно, оскільки область застосування сценарію обмежена. Я думаю, що єдиною причиною, яка дозволяє вам робити це в сценаріях, є підтримка DSL так, як це робить Рубі.


5

Насправді, я не думаю, що так би поводилося ...

змінні у Groovy все ще потребують декларації, тільки не TYPED-декларація, оскільки права частина, як правило, містить достатньо інформації для Groovy для введення змінної.

Коли я намагаюся використовувати змінну, яку я не декларував із def або type, я отримую помилку "Немає такої властивості", оскільки передбачає, що я використовую член класу, що містить код.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.