Groovy: mục đích của dịch vụ def def trong trò chơi def x = 0 là gì?


180

Trong đoạn mã sau (lấy từ trang Hướng dẫn ngữ nghĩa Groovy ), tại sao lại thêm tiền tố vào từ khóa def?

def x = 0
def y = 5

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

assert x == 5

Các deftừ khóa có thể được gỡ bỏ, và đoạn mã này sẽ tạo ra kết quả tương tự. Vậy tác dụng của từ khóa là defgì?

Câu trả lời:


278

Đó là cú pháp đường cho các kịch bản cơ bản. Việc bỏ qua từ khóa "def" sẽ đặt biến trong các ràng buộc cho tập lệnh hiện tại và Groovy xử lý nó (phần lớn) giống như một biến có phạm vi toàn cầu:

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

Thay vào đó, sử dụng từ khóa def không đặt biến trong các ràng buộc của tập lệnh:

def y = 2

assert y == 2

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

In: "lỗi bắt"

Sử dụng từ khóa def trong các chương trình lớn hơn rất quan trọng vì nó giúp xác định phạm vi có thể tìm thấy biến và có thể giúp duy trì đóng gói.

Nếu bạn xác định một phương thức trong tập lệnh của mình, nó sẽ không có quyền truy cập vào các biến được tạo bằng "def" trong phần thân của tập lệnh chính vì chúng không có trong phạm vi:

 x = 1
 def y = 2


public bar() {
    assert x == 1

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

bar()

in "lỗi bắt"

Biến "y" không nằm trong phạm vi bên trong hàm. "x" nằm trong phạm vi vì Groovy sẽ kiểm tra các ràng buộc của tập lệnh hiện tại cho biến. Như tôi đã nói trước đây, đây chỉ đơn giản là cú pháp đường để làm cho các kịch bản nhanh và bẩn nhanh hơn để loại ra (thường là một lớp lót).

Thực hành tốt trong các tập lệnh lớn hơn là luôn sử dụng từ khóa "def" để bạn không gặp phải các vấn đề phạm vi lạ hoặc can thiệp vào các biến bạn không có ý định.


36

Câu trả lời của Ted là tuyệt vời cho các kịch bản; Câu trả lời của Ben là tiêu chuẩn cho các lớp học.

Như Ben nói, hãy nghĩ về nó như là "Đối tượng" - nhưng nó thú vị hơn ở chỗ nó không ràng buộc bạn với các phương thức Đối tượng. Điều này có ý nghĩa gọn gàng đối với hàng nhập khẩu.

ví dụ: Trong đoạn trích này, tôi phải nhập 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()

ví dụ: Nhưng ở đây tôi chỉ có thể 'chắp cánh' miễn là mọi thứ đều nằm trên đường dẫn

// 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
Tại sao bạn được phép new FileInputStream('Test.groovy').getChannel()nhập khẩu?
Alexander Suraphel

3
@AlexanderSuraphel "miễn là mọi thứ đều nằm trên đường dẫn"
Hanno

30

Theo trang này , deflà một thay thế cho một loại tên và có thể chỉ đơn giản được coi là bí danh cho Object(nghĩa là bạn không quan tâm đến loại đó).


12

Đối với kịch bản duy nhất này có liên quan không có sự khác biệt thực tế.

Tuy nhiên, các biến được xác định bằng từ khóa "def" được coi là biến cục bộ, nghĩa là cục bộ của tập lệnh này. Các biến không có "def" phía trước chúng được lưu trữ trong cái gọi là ràng buộc khi sử dụng lần đầu. Bạn có thể nghĩ về ràng buộc như là một vùng lưu trữ chung cho các biến và các bao đóng cần có sẵn "giữa" các tập lệnh.

Vì vậy, nếu bạn có hai tập lệnh và thực thi chúng với cùng GroovyShell, tập lệnh thứ hai sẽ có thể nhận được tất cả các biến được đặt trong tập lệnh đầu tiên mà không có "def".


8

Lý do cho "def" là để nói với Groovy rằng bạn có ý định tạo một biến ở đây. Điều này rất quan trọng vì bạn không bao giờ muốn tạo một biến số một cách tình cờ.

Nó có thể chấp nhận được trong các tập lệnh (kịch bản Groovy và Groovysh cho phép bạn làm như vậy), nhưng trong mã sản xuất, đó là một trong những tệ nạn lớn nhất bạn có thể gặp phải đó là lý do tại sao bạn phải xác định một biến có def trong tất cả mã Groovy thực tế (bất cứ điều gì bên trong một lớp học).

Đây là một ví dụ về lý do tại sao nó xấu. Điều này sẽ chạy (Không bị xác nhận) nếu bạn sao chép mã sau đây và dán mã vào Groovysh:

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

Loại vấn đề này có thể mất rất nhiều thời gian để tìm và khắc phục - Ngay cả khi nó chỉ cắn bạn một lần trong đời, nó vẫn sẽ tốn nhiều thời gian hơn so với việc tuyên bố rõ ràng các biến số hàng ngàn lần trong suốt sự nghiệp của bạn. Nó cũng trở nên rõ ràng trước mắt nơi nó được tuyên bố, bạn không cần phải đoán.

Trong các tập lệnh / bảng điều khiển không quan trọng (như bảng điều khiển Groovy), điều đó có thể chấp nhận được vì phạm vi của tập lệnh bị giới hạn. Tôi nghĩ lý do duy nhất Groovy cho phép bạn thực hiện điều này trong các tập lệnh là để hỗ trợ DSL theo cách mà Ruby làm (Một sự đánh đổi tồi tệ nếu bạn hỏi tôi, nhưng một số người yêu thích DSL)


5

Thật ra, tôi không nghĩ nó sẽ hành xử như vậy ...

các biến trong Groovy vẫn yêu cầu khai báo, chỉ không khai báo TYPED, vì phía bên phải thường chứa đủ thông tin để Groovy nhập biến.

Khi tôi cố gắng sử dụng một biến mà tôi chưa khai báo với def hoặc một loại, tôi gặp lỗi "Không có thuộc tính như vậy", vì nó giả sử rằng tôi đang sử dụng một thành viên của lớp có chứa mã.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.