2015-03-16 18 views
15

Câu hỏi đơn giản về cú pháp java-8. Tại sao JLS-8 hạn chế biểu hiện như vậy như:Tại sao tôi không thể gán trực tiếp tham chiếu phương thức cho biến kiểu Đối tượng?

Object of_ref = Stream::of; // compile-time error 

và chỉ cho phép một cái gì đó như:

java.util.function.Function of_ref = Stream::of; 
Object obj = of_ref; // compiles ok 

?

+4

Trong đoạn mã không thể nén đầu tiên của bạn, bạn mong đợi điều gì sẽ được lưu trữ trong 'of_ref'? Loại bê tông của cá thể được tham chiếu là gì? –

Trả lời

7

Đó là vì loại đích của tham chiếu phương thức hoặc biểu thức lambda phải là giao diện chức năng. Chỉ dựa trên đó, thời gian chạy sẽ tạo ra một cá thể của một lớp cung cấp việc thực hiện giao diện chức năng đã cho. Hãy suy nghĩ về lambdas hoặc tham chiếu phương thức như khái niệm abstract. Chỉ định nó cho một loại giao diện chức năng mang lại cho nó một ý nghĩa cụ thể.

Hơn nữa, một tham chiếu lambda hoặc phương thức cụ thể, có thể có nhiều giao diện chức năng làm loại mục tiêu của nó. Ví dụ, hãy xem xét lamda sau:

int x = 5; 
FunctionalInterface func = (x) -> System.out.println(x); 

lambda Đây là một Consumer của x. Ngoài ra, bất kỳ giao diện nào có phương thức trừu tượng đơn lẻ có chữ ký sau:

public abstract void xxx(int value); 

có thể được sử dụng làm loại mục tiêu. Vì vậy, giao diện nào bạn muốn thời gian chạy để triển khai, nếu bạn gán lambda cho loại Object? Đó là lý do tại sao bạn đã cung cấp một giao diện chức năng rõ ràng làm loại mục tiêu.

Bây giờ, khi bạn nhận được một tài liệu tham khảo giao diện chức năng tổ chức một ví dụ, bạn có thể gán nó cho bất kỳ tài liệu tham khảo siêu (bao gồm Object)

+0

Cảm ơn, nhưng nó nghe có vẻ không hợp lý: tôi có thể chuyển 'obj' trở lại' Hàm' và gọi 'apply()', vì vậy chúng ta không thể làm điều này trực tiếp khi lấy tham chiếu phương thức? – Andremoniy

+2

@Andremoniy Để chuyển 'obj' trở lại thành' Hàm', trước tiên bạn phải tạo một 'đối tượng' đúng không? Điều gì sẽ là loại của 'đối tượng'? –

+0

Ah, ok, tôi hiểu được logic của bạn. – Andremoniy

8

Object không phải là giao diện chức năng và chỉ có thể gán tham chiếu phương thức cho giao diện chức năng. Xem ví dụ JLS #15.13.2

Một biểu thức phương pháp tham chiếu là tương thích trong một bối cảnh chuyển nhượng, bối cảnh gọi, hoặc bối cảnh đúc với một loại mục tiêu T nếu T là một kiểu giao diện chức năng (§9.8) và sự biểu hiện là đồng dư với loại chức năng của loại mục tiêu mặt đất bắt nguồn từ T.

+0

Cảm ơn, nhưng tôi hỏi tại sao tôi không thể gán trực tiếp tham chiếu này cho 'Object'? – Andremoniy

+4

Vì 'Đối tượng', được biểu diễn bằng' T' trong trích dẫn JLS, không phải là loại giao diện chức năng. –

3

tôi nghi ngờ đây là một câu hỏi hoàn toàn academical, vì tôi không thể nhìn thấy bất kỳ thực -trường hợp sử dụng cho cuộc sống này. Tuy nhiên, tôi khá chắc chắn rằng nó đã làm với Stream::of là một biểu thức lambda. Bạn cũng có thể không làm điều này:

Object of_ref = list -> Stream.of(list); 

Tôi suy đoán rằng một kiểu trả về chính xác cho trình biên dịch mà FunctionalInterface nó đang được sử dụng. Nếu không có thông tin này, trình biên dịch sẽ không thể giải quyết biểu thức Lambda một cách chính xác và rõ ràng.

4

Điểm mấu chốt là không có "loại chức năng" bằng Java. Một biểu thức lambda không có một "loại" của chính nó - nó có thể được gõ vào bất kỳ giao diện chức năng có chữ ký của phương thức duy nhất phù hợp với lambda. Do đó, kiểu lambda dựa trên kiểu được cung cấp bởi ngữ cảnh của nó. Bạn phải cung cấp một giao diện chức năng như bối cảnh cho nó để có được một loại.

Đó là hướng dẫn để xem xét cùng một vấn đề nhưng đối với các lớp ẩn danh. Mặc dù có sự khác biệt về triển khai giữa lambdas và các lớp ẩn danh, về mặt ngữ nghĩa, lambdas về cơ bản tương đương với một tập con của các lớp ẩn danh, và biểu thức lambda luôn có thể được chuyển thành biểu thức tạo lớp ẩn danh tương đương.

Khi bạn viết:

Function<T, Stream<T>> of_ref = Stream::of; 

nó tương đương với một cái gì đó giống như sử dụng các lớp nặc danh sau:

Function<T, Stream<T>> of_ref = new Function<T, Stream<T>>() { 
    Stream<T> apply(T t) { 
     return Stream.of(t); 
    } 
}; 

Bây giờ xem xét

Object of_ref = Stream::of; 

tương đương với các lớp ẩn danh là gì ?

Object of_ref = new [**What goes here?**]() { 
    [**What method signature goes here?**] { 
     return Stream.of(t); 
    } 
}; 

Bạn thấy tại sao điều đó không có ý nghĩa - chúng tôi không biết loại nào sẽ sử dụng làm lớp cơ sở của lớp ẩn danh.

+0

Ok! Điều này có nghĩa rằng nó chỉ là cú pháp, đúng không? – Andremoniy

+0

@Andremoniy: Vâng, nó phụ thuộc vào cách bạn xác định "cú pháp đường". Trong một số trường hợp, một số chuyển đổi là cần thiết (ví dụ 'this' trong lambda tương đương với' OuterClass.this', không phải 'this', trong một lớp ẩn danh), và có một số khác biệt hành vi, như hai đối tượng lớp ẩn danh khác nhau (' ! = '), nhưng hai lambdas có thể không. Nhưng đối với hầu hết các phần bạn có thể nghĩ về nó như là một cú pháp cú pháp. – newacct

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