Groovy: apa tujuan dari "def" di "def x = 0"?

180

Di bagian kode berikut (diambil dari halaman Manual Semantik Groovy ), mengapa awalan tugas dengan kata kunci def?

def x = 0
def y = 5

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

assert x == 5

Kata defkunci dapat dihapus, dan cuplikan ini akan menghasilkan hasil yang sama. Jadi apa efek dari kata kunci def?

Leonel
sumber

Jawaban:

278

Ini gula sintaksis untuk skrip dasar. Menghilangkan kata kunci "def" menempatkan variabel di binding untuk skrip saat ini dan asyik memperlakukannya (kebanyakan) seperti variabel cakupan global:

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

Sebaliknya, menggunakan kata kunci def tidak menempatkan variabel dalam binding skrip:

def y = 2

assert y == 2

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

Cetakan: "kesalahan tertangkap"

Menggunakan kata kunci def dalam program yang lebih besar adalah penting karena ini membantu menentukan ruang lingkup di mana variabel dapat ditemukan dan dapat membantu menjaga enkapsulasi.

Jika Anda mendefinisikan metode dalam skrip Anda, itu tidak akan memiliki akses ke variabel yang dibuat dengan "def" di tubuh skrip utama karena mereka tidak dalam ruang lingkup:

 x = 1
 def y = 2


public bar() {
    assert x == 1

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

bar()

mencetak "kesalahan tertangkap"

Variabel "y" tidak berada dalam ruang lingkup di dalam fungsi. "x" berada dalam ruang lingkup karena groovy akan memeriksa binding skrip saat ini untuk variabel. Seperti yang saya katakan sebelumnya, ini hanyalah gula sintaksis untuk membuat skrip cepat dan kotor lebih cepat untuk mengetik (sering satu baris).

Praktik yang baik dalam skrip yang lebih besar adalah selalu menggunakan kata kunci "def" sehingga Anda tidak mengalami masalah pelingkupan yang aneh atau mengganggu variabel yang tidak Anda inginkan.

Ted Naleid
sumber
36

Jawaban Ted sangat bagus untuk skrip; Jawaban Ben adalah standar untuk kelas.

Seperti kata Ben, anggap itu sebagai "Objek" - tetapi jauh lebih keren karena itu tidak membatasi Anda pada metode Objek. Ini memiliki implikasi yang rapi sehubungan dengan impor.

mis. Dalam cuplikan ini saya harus mengimpor 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()

mis. Tapi di sini saya hanya bisa 'sayap itu' selama semuanya ada di 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
sumber
1
mengapa Anda diizinkan new FileInputStream('Test.groovy').getChannel()tanpa impor?
Alexander Suraphel
3
@AlexanderSuraphel "selama semuanya ada di jalan setapak"
Hanno
30

Menurut halaman ini , defadalah pengganti untuk nama jenis dan hanya dapat dianggap sebagai alias untuk Object(yaitu menandakan bahwa Anda tidak peduli dengan jenisnya).

Ben Hoffstein
sumber
12

Sejauh menyangkut naskah tunggal ini, tidak ada perbedaan praktis.

Namun, variabel yang didefinisikan menggunakan kata kunci "def" diperlakukan sebagai variabel lokal, yaitu lokal untuk skrip yang satu ini. Variabel tanpa "def" di depannya disimpan dalam apa yang disebut binding setelah digunakan pertama kali. Anda dapat menganggap pengikatan sebagai area penyimpanan umum untuk variabel dan penutupan yang harus tersedia skrip "antara".

Jadi, jika Anda memiliki dua skrip dan menjalankannya dengan GroovyShell yang sama, skrip kedua akan bisa mendapatkan semua variabel yang ditetapkan dalam skrip pertama tanpa "def".


sumber
8

Alasan untuk "def" adalah untuk memberi tahu groovy bahwa Anda bermaksud membuat variabel di sini. Ini penting karena Anda tidak ingin membuat variabel secara tidak sengaja.

Ini agak dapat diterima dalam skrip (skrip Groovy dan groovysh memungkinkan Anda untuk melakukannya), tetapi dalam kode produksi itu adalah salah satu kejahatan terbesar yang dapat Anda temui yang mengapa Anda harus mendefinisikan variabel dengan def dalam semua kode groovy aktual (apa pun di dalam kelas).

Inilah contoh mengapa itu buruk. Ini akan berjalan (Tanpa gagal menegaskan) jika Anda menyalin kode berikut dan menempelkannya ke groovysh:

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

Masalah seperti ini bisa membutuhkan banyak waktu untuk menemukan dan memperbaikinya - Bahkan jika itu hanya menggigit Anda sekali dalam hidup Anda, masih akan memakan waktu lebih banyak daripada secara eksplisit mendeklarasikan variabel ribuan kali sepanjang karier Anda. Itu juga menjadi jelas bagi mata di mana itu dinyatakan, Anda tidak perlu menebak.

Dalam input skrip / konsol yang tidak penting (seperti konsol asyik) itu agak dapat diterima karena ruang lingkup skrip terbatas. Saya pikir satu-satunya alasan groovy memungkinkan Anda melakukan ini dalam skrip adalah untuk mendukung DSL seperti halnya Ruby (trade-off yang buruk jika Anda bertanya kepada saya, tetapi beberapa orang menyukai DSL)

Bill K.
sumber
5

Sebenarnya, saya tidak berpikir itu akan berperilaku sama ...

variabel dalam Groovy masih memerlukan deklarasi, hanya saja deklarasi TYPED, karena sisi kanan umumnya berisi informasi yang cukup untuk Groovy untuk mengetik variabel.

Ketika saya mencoba menggunakan variabel yang belum saya deklarasikan dengan def atau tipe, saya mendapatkan kesalahan "Tidak ada properti seperti itu", karena mengasumsikan bahwa saya menggunakan anggota kelas yang berisi kode.

billjamesdev
sumber