2015-03-28 19 views
22

Tôi nhận được một lỗi trên mã sau đây, mà tôi tin rằng không nên có ... Sử dụng JDK 8u40 để biên dịch mã này.Tham chiếu đến phương pháp không rõ ràng khi sử dụng lambdas và generics

public class Ambiguous { 
    public static void main(String[] args) { 
     consumerIntFunctionTest(data -> { 
      Arrays.sort(data); 
     }, int[]::new); 

     consumerIntFunctionTest(Arrays::sort, int[]::new); 
    } 

    private static <T> void consumerIntFunctionTest(final Consumer<T> consumer, final IntFunction<T> generator) { 

    } 

    private static <T> void consumerIntFunctionTest(final Function<T, ?> consumer, final IntFunction<T> generator) { 

    } 
} 

Các lỗi như sau:

Error:(17, 9) java: reference to consumerIntFunctionTest is ambiguous both method consumerIntFunctionTest(java.util.function.Consumer,java.util.function.IntFunction) in net.tuis.ubench.Ambiguous and method consumerIntFunctionTest(java.util.function.Function,java.util.function.IntFunction) in net.tuis.ubench.Ambiguous match

Các lỗi xảy ra trên dòng sau:

consumerIntFunctionTest(Arrays::sort, int[]::new); 

Tôi tin rằng không nên có lỗi, như tất cả các tài liệu tham khảo Arrays::sort là kiểu void và không ai trong số họ trả về giá trị. Như bạn có thể quan sát, nó hiện hoạt động khi tôi mở rộng rõ ràng lambda Consumer<T>.

Đây có phải là lỗi trong javac hay trạng thái JLS không thể tự động mở rộng lambda trong trường hợp này? Nếu nó là sau này, tôi vẫn sẽ nghĩ rằng nó là lạ, như consumerIntFunctionTest với đối số đầu tiên Function<T, ?> không nên phù hợp.

+2

Địa điểm trong JLS nơi vị trí này cần được xác định là [15.27.3] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27. 3). (Chưa xem chi tiết). – Jesper

+3

Tại sao bạn nghĩ 'Chức năng ' không khớp? '?' cũng có thể là 'Void', vì vậy nó khớp với nhau. – tomse

+0

@tomse 'Void' là kiểu trả về không liên quan gì đến' void' làm kiểu phương thức, rằng 'Void' chỉ là một đối tượng. – skiwi

Trả lời

7

Trong ví dụ đầu tiên bạn

consumerIntFunctionTest(data -> { 
     Arrays.sort(data); 
    }, int[]::new); 

biểu thức lambda có một khối -tương thích void có thể được xác định bởi cấu trúc của biểu thức mà không cần phải giải quyết các loại thực tế.

Ngược lại, trong ví dụ

consumerIntFunctionTest(Arrays::sort, int[]::new); 

tài liệu tham khảo phương pháp đã được giải quyết để tìm hiểu, cho dù đó phù hợp với một trong hai, một chức năng void (Consumer) hoặc một hàm giá trị trở về (Function). Điều tương tự cũng áp dụng cho các biểu thức lambda đơn giản

consumerIntFunctionTest(data -> Arrays.sort(data), int[]::new); 

mà có thể là cả hai, void - tương thích hoặc giá trị gia tăng phù hợp, tùy thuộc vào phương pháp mục tiêu giải quyết.

Vấn đề là giải quyết phương thức đòi hỏi kiến ​​thức về chữ ký bắt buộc, cần được xác định thông qua nhập mục tiêu, nhưng loại mục tiêu không được biết cho đến khi thông số loại của phương thức chung được biết. Trong khi trên lý thuyết cả hai có thể được xác định cùng một lúc, quá trình (vẫn đang rất phức tạp) đã được đơn giản hóa trong đặc điểm kỹ thuật trong phương pháp đó quá tải độ phân giải được thực hiện đầu tiên và loại suy luận được áp dụng cuối cùng (xem JLS §15.12.2). Do đó, thông tin mà suy luận kiểu có thể cung cấp không thể được sử dụng để giải quyết vấn đề quá tải.

Nhưng lưu ý rằng bước đầu tiên được mô tả trong 15.12.2.1. Identify Potentially Applicable Methods chứa:

An expression is potentially compatible with a target type according to the following rules:

  • A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true:

    • The arity of the target type's function type is the same as the arity of the lambda expression.

    • If the target type's function type has a void return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2).

    • If the target type's function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2).

  • A method reference expression (§15.13) is potentially compatible with a functional interface type if, where the type's function type arity is n, there exists at least one potentially applicable method for the method reference expression with arity n (§15.13.1), and one of the following is true:

    • The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is i) static and supports arity n, or ii) not static and supports arity n-1.

    • The method reference expression has some other form and at least one potentially applicable method is not static.

The definition of potential applicability goes beyond a basic arity check to also take into account the presence and "shape" of functional interface target types. In some cases involving type argument inference, a lambda expression appearing as a method invocation argument cannot be properly typed until after overload resolution.

Vì vậy, trong ví dụ đầu tiên một trong những phương pháp được sắp xếp ra bởi hình dạng của lambda trong khi trong trường hợp một tài liệu tham khảo phương pháp hoặc một biểu thức lambda gồm của một biểu thức invocation duy nhất, cả hai phương thức có khả năng áp dụng quy trình lựa chọn đầu tiên này và mang lại lỗi “mơ hồ” trước khi suy luận kiểu có thể bắt đầu để hỗ trợ tìm phương pháp đích để xác định xem đó là phương thức trả về giá trị void hay giá trị.

Lưu ý rằng giống như sử dụng x->{ foo(); } để thực hiện biểu thức lambda rõ ràng void tương thích, bạn có thể sử dụng x->(foo()) để làm cho biểu thức lambda tương thích giá trị rõ ràng.


Bạn điên hơn nữa đọc this answer giải thích rằng hạn chế này của kết hợp kiểu suy luận và giải quyết tình trạng quá tải phương pháp là một quyết định cố ý (nhưng không hề dễ dàng).

+0

Điều này cũng có thể giải thích tại sao tôi nhận được cùng một lỗi nếu tôi loại bỏ đối số '' và thay thế nó bằng 'int []' trong cả hai tham số? Điều đó dường như giải quyết vấn đề generics, nhưng vẫn cho rằng lỗi. – skiwi

+1

Như được xây dựng trong câu trả lời được liên kết (xem ví dụ 'so sánh'), kiểu trả về của tham chiếu biểu thức/phương thức lambda không được xem xét trong quá trình phân giải quá tải.Lưu ý rằng các trình biên dịch gần đây sẽ cung cấp cho bạn một cảnh báo về sự mơ hồ tiềm ẩn của các phương thức quá tải ngay tại vị trí khai báo mà không cần phải tìm ra thông qua các lời gọi thực sự mơ hồ. Bạn biết "loại mục tiêu không được biết" áp dụng cho trình biên dịch (tuân thủ quy trình chính thức) không cho chúng tôi người đọc của con người và không yêu cầu Generics mà chỉ cần đặt hàng nghiêm ngặt các bước giải quyết. – Holger

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