2011-01-29 37 views
14

Sau khi xác định một bản ghi và các giao diện nó thực hiện, tôi có thể gọi các phương thức của nó bằng tên của nó hoặc sử dụng cách tương tác java bằng cách sử dụng toán tử dấu chấm.Độ phân giải tên phương thức defrecord của clojure hoạt động như thế nào?

user=> (defprotocol Eat (eat [this])) 
Eat 
user=> (defrecord animal [name] Eat (eat [this] "eating")) 
user.animal 
user=> (eat (animal. "bob")) 
"eating" 
user=> (.eat (animal. "bob")) 
"eating" 
user=> 

Dưới bề mặt, những gì đang diễn ra ở đó? Có chức năng clojure mới đang được xác định? Điều gì sẽ xảy ra khi có các hàm bạn đã xác định có cùng tên (điều này có thể không?), Các sự mơ hồ này được giải quyết như thế nào?

Ngoài ra, có thể "nhập" các phương thức java cho các đối tượng java khác để bạn không cần. nhà điều hành để hành vi đó giống như trên? (Ví dụ, để hợp nhất giao diện người dùng)

+0

sở thích phong cách của tôi là để tránh những int Java erop hình thức cho cả hai hồ sơ (constructors) và giao thức (cuộc gọi). Trong trường hợp này: (ăn (-> động vật "bob")) –

Trả lời

23

Khi bạn định nghĩa một giao thức, mỗi phương thức của nó được tạo thành các hàm trong các không gian tên hiện tại của bạn. Sau đó bạn không thể có hai giao thức xác định cùng một hàm trong cùng một không gian tên. Nó cũng có nghĩa là bạn có thể có chúng trong các không gian tên riêng biệt và một kiểu nhất định có thể mở rộng cả hai [1] của chúng mà không có bất kỳ sự kiện nào bởi vì chúng được đặt tên (đối lập với Java, nơi một lớp không thể thực hiện hai giao diện với các phương thức đồng âm) .

Từ góc độ người dùng, phương thức giao thức không khác với các hàm không đa hình cũ thuần túy.

Thực tế là bạn có thể gọi phương thức giao thức bằng cách sử dụng interop là chi tiết triển khai. Lý do cho điều đó là đối với mỗi giao thức, trình biên dịch Clojure tạo ra một giao diện sao lưu tương ứng. Sau này khi bạn định nghĩa một kiểu mới với các phần mở rộng giao thức nội tuyến, thì kiểu này sẽ thực hiện các giao diện sao lưu của các giao thức này.

Do đó bạn không thể sử dụng mẫu interop vào một đối tượng mà phần mở rộng chưa được cung cấp nội tuyến:

(defrecord VacuumCleaner [brand model] 
(extend-protocol Eat 
    VacuumCleaner 
    (eat [this] "eating legos and socks")) 

(.eat (VaacumCleaner. "Dyson" "DC-20")) 
; method not found exception 

Trình biên dịch đã hỗ trợ đặc biệt cho các chức năng giao thức để họ được biên soạn như một séc dụ theo sau là cuộc gọi phương thức ảo, do đó, khi áp dụng (eat ...) sẽ nhanh như (.eat ...).

Để trả lời "có phương pháp java một nhập khẩu", bạn có thể bọc chúng trong FNS thường xuyên:

(def callme #(.callme %1 %2 %3)) 

(rõ ràng là bạn có thể cần thêm arities khác để chiếm quá tải và loại gợi ý để loại bỏ sự phản xạ)

[1] tuy nhiên bạn không thể mở rộng cả inline (ít nhất một trong số họ phải nằm trong một hình thức extend-*), vì một giới hạn thực hiện

+0

Xin cảm ơn, điều đó làm sáng tỏ rất nhiều. – bmillare

Các vấn đề liên quan