2009-02-01 30 views
31

Tôi nhận thấy hôm nay rằng tự động đấm bốc đôi khi có thể gây ra sự mơ hồ trong phương pháp phân giải quá tải. Ví dụ đơn giản nhất dường như là điều này:Tại sao autoboxing thực hiện một số cuộc gọi mơ hồ trong Java?

public class Test { 
    static void f(Object a, boolean b) {} 
    static void f(Object a, Object b) {} 

    static void m(int a, boolean b) { f(a,b); } 
} 

Khi biên soạn, nó gây ra các lỗi sau:

Test.java:5: reference to f is ambiguous, both method 
    f(java.lang.Object,boolean) in Test and method 
    f(java.lang.Object,java.lang.Object) in Test match 

static void m(int a, boolean b) { f(a, b); } 
           ^

Việc sửa chữa lỗi này là tầm thường: chỉ cần sử dụng rõ ràng auto-boxing:

static void m(int a, boolean b) { f((Object)a, b); } 

Gọi chính xác quá tải đầu tiên như mong đợi.

Vậy tại sao độ phân giải quá tải thất bại? Tại sao trình biên dịch không tự động chọn đối số đầu tiên, và chấp nhận đối số thứ hai bình thường? Tại sao tôi phải yêu cầu auto-boxing một cách rõ ràng?

Trả lời

31

Khi bạn cast đối số đầu tiên vào Object mình, trình biên dịch sẽ phù hợp với phương pháp này mà không sử dụng autoboxing (JLS3 15.12.2):

The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

Nếu bạn không cast nó một cách rõ ràng, nó sẽ đi đến giai đoạn thứ hai của cố gắng để tìm một phương pháp phù hợp, cho phép autoboxing, và sau đó nó thực sự là mơ hồ, bởi vì đối số thứ hai của bạn có thể được kết hợp bởi boolean hoặc Object.

The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation.

Tại sao, trong giai đoạn thứ hai, trình biên dịch không chọn phương pháp thứ hai vì không có autoboxing đối số boolean là cần thiết? Bởi vì sau khi nó đã tìm thấy hai phương pháp phù hợp, chỉ có chuyển đổi loại phụ được sử dụng để xác định phương pháp cụ thể nhất của hai, bất kể bất kỳ đấm bốc hoặc unboxing nào diễn ra để phù hợp với chúng ngay từ đầu (§15.12.2.5).

Ngoài ra: trình biên dịch không phải lúc nào cũng chọn phương pháp cụ thể nhất dựa trên số lượng auto (un) boxing cần thiết. Nó vẫn có thể dẫn đến các trường hợp mơ hồ. Ví dụ: điều này vẫn còn mơ hồ:

public class Test { 
    static void f(Object a, boolean b) {} 
    static void f(int a, Object b) {} 

    static void m(int a, boolean b) { f(a, b); } // ambiguous 
} 

Hãy nhớ rằng thuật toán chọn phương thức đối sánh (bước biên dịch 2) được cố định và mô tả trong JLS. Một lần trong giai đoạn 2 không có hộp chọn tự động hoặc unboxing chọn lọc. Trình biên dịch sẽ định vị tất cả các phương thức có thể truy cập (cả hai phương thức trong các trường hợp này) và áp dụng (một lần nữa hai phương thức), và sau đó chỉ chọn phương thức cụ thể nhất mà không cần nhìn vào boxing/unboxing.

+1

Cảm ơn bạn @eljenso. Điều này làm rõ vấn đề của trình biên dịch, nhưng sau đó nó làm cho tôi tự hỏi tại sao giai đoạn thứ hai được định nghĩa là như vậy. Nó có thể được sửa đổi bằng cách "làm ít nhất có thể số lượng chuyển đổi quyền anh/unboxing" không? –

+0

Câu trả lời hay! –

+0

Vâng, cảm ơn bạn @eljenso! Tôi đồng ý rằng, trong ví dụ của bạn, cuộc gọi không rõ ràng. Tôi sẽ nghĩ rằng hai tình trạng quá tải sẽ có cùng số lượng chuyển đổi quyền anh, vì vậy cuộc gọi thực sự là mơ hồ. Nhưng (bất kể JLS), tôi không thấy ví dụ của tôi mơ hồ. Bạn nghĩ sao? –

2

So why did the overload resolution fail? Why didn't the compiler auto-box the first argument, and accept the second argument normally? Why did I have to request auto-boxing explicitly?

Nó không chấp nhận đối số thứ hai bình thường. Hãy nhớ rằng "boolean" có thể được đóng hộp cho một đối tượng quá. Bạn có thể rõ ràng đã đưa đối số boolean vào Object và nó sẽ hoạt động.

+0

Cảm ơn @Kevin. Có, tôi có thể đóng hộp nó một cách rõ ràng, nhưng tôi thì không. Vậy tại sao nó không chọn quá tải cụ thể nhất, mà trong trường hợp này là trường hợp đầu tiên? –

+0

Bởi vì trình biên dịch không có đủ thông tin để đưa ra quyết định đó cho bạn. * Cả hai * phương pháp được áp dụng bởi vì boolean có thể được đóng hộp cho một đối tượng – Kevin

+0

Tôi không nghĩ rằng điều này trả lời câu hỏi. Khi áp dụng nhiều phương pháp, một phương thức yêu cầu số lượng chuyển đổi mở rộng tối thiểu được chọn tự động. Bạn nhận được một lỗi mơ hồ chỉ khi nhiều phương pháp cần cùng một số "mở rộng". Tôi đoán là boxing không được tính là mở rộng. – erickson

3

Khi bạn nói f (a, b), trình biên dịch bị nhầm lẫn về chức năng cần tham chiếu đến.

Điều này là do một là một int, nhưng lập luận dự kiến ​​trong f là một Object. Vì vậy, trình compliler quyết định chuyển đổi a thành một đối tượng. Bây giờ vấn đề là, nếu a có thể được chuyển đổi thành một đối tượng, vì vậy có thể là b.

Điều này có nghĩa là cuộc gọi hàm có thể tham chiếu đến một trong hai định nghĩa. Điều này làm cho cuộc gọi không rõ ràng.

Khi bạn chuyển đổi a thành đối tượng theo cách thủ công, trình biên dịch chỉ tìm kiếm đối sánh gần nhất và sau đó tham chiếu đến đối tượng đó.

Why didn't the compiler select the function that can be reached by "doing the least possible number of boxing/unboxing conversions"?

Xem các trường hợp sau đây:

f(boolean a, Object b) 
f(Object a , boolean b) 

Nếu chúng ta gọi là như f (boolean một, boolean b), mà chức năng đó nên chọn? Nó mơ hồ phải không? Tương tự, điều này sẽ trở nên phức tạp hơn khi có rất nhiều đối số. Vì vậy, trình biên dịch đã chọn để cung cấp cho bạn một cảnh báo thay thế.

Vì không có cách nào để biết một trong những chức năng mà lập trình viên thực sự muốn gọi, trình biên dịch đưa ra lỗi.

+0

Cảm ơn @Niyaz. Nhưng ngay cả khi a và b có thể được chuyển đổi thành các đối tượng, thì đó là _không cần thiết_. Vì vậy, trình biên dịch (IMHO) phải chuyển đổi một đối tượng, nhưng đối với b nó nên chọn quá tải cụ thể nhất, đó là quá tải đầu tiên. Có gì sai với logic này? –

+0

Nếu có nhiều đối số hơn trong ví dụ đã cho, trình biên dịch chọn "quá tải cụ thể nhất" như thế nào? Đó là vấn đề. – Niyaz

+0

Niyaz. sự khác biệt sẽ làm gì nếu anh ta chuyển sang Object? cả hai hàm đều chấp nhận Object. vì vậy tôi sẽ nói theo bản năng rằng nó không thực hiện bất kỳ trận đấu nào tốt hơn. –

4

Trình biên dịch đã làm tự động chọn đối số đầu tiên. Một khi đã được thực hiện, đó là đối số thứ hai đó là mơ hồ, vì nó có thể được xem như là boolean hoặc Object.

This page giải thích các quy tắc cho autoboxing và chọn phương thức để gọi.Trình biên dịch đầu tiên cố gắng chọn phương thức mà không cần sử dụng bất kỳ hộp tự động nào tại tất cả, bởi vì hình phạt thực hiện quyền anh và unboxing mang theo. Nếu không có phương pháp nào có thể được lựa chọn mà không cần đến boxing, như trong trường hợp này, thì boxing nằm trên bàn cho tất cả các đối số cho phương thức đó.

+0

Trong trường hợp đó, sẽ không f (Object, boolean) là phương pháp "cụ thể" hơn? –

+0

Thanks @Bill. @Zach hỏi tôi cùng một câu hỏi. –

+0

Tôi đã chỉnh sửa câu trả lời của mình để trả lời câu hỏi đó. –

2

Xem http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448

Dàn diễn viên giúp vì sau đó không đấm bốc là cần thiết để tìm ra phương pháp để gọi. Nếu không có diễn viên thử thứ hai là để cho phép đấm bốc và sau đó cũng boolean có thể được đóng hộp.

Tốt hơn là có thông số kỹ thuật rõ ràng và dễ hiểu để nói điều gì sẽ xảy ra hơn để khiến mọi người đoán.

+0

như vậy, nó giống như "một khi tự động đóng hộp, chúng tôi có thể đi bẩn với các đối số khác quá" và "nếu không có hộp tự động xảy ra được nêu ra, chúng tôi cố gắng không để autobox người khác quá"? –

+0

Cảm ơn bạn @iny. Tôi xin lỗi vì đã bị bỏ phiếu cho hôm nay! –

+0

@ litb, vâng có vẻ như vậy. Nhưng tôi tự hỏi tại sao họ không cố gắng "thực hiện số lượng chuyển đổi boxing/unboxing ít nhất có thể". –

1

Trình biên dịch Java giải quyết các phương thức quá tải và các hàm tạo trong các giai đoạn. Trong giai đoạn đầu tiên [§15.12.2.2], nó xác định các phương pháp áp dụng bằng cách phân nhóm [§4.10]. Trong ví dụ này, không có phương thức nào được áp dụng, bởi vì int không phải là một kiểu con của Object.

Trong giai đoạn thứ hai [§15.12.2.3], trình biên dịch xác định các phương thức áp dụng bằng cách chuyển đổi lời gọi phương thức [§5.3], là sự kết hợp giữa hộp số tự động và kiểu con. Đối số int có thể được chuyển đổi thành một số nguyên, là một kiểu con của đối tượng, cho cả hai tình trạng quá tải. Đối số boolean không cần chuyển đổi cho quá tải đầu tiên, và có thể được chuyển đổi sang Boolean, một kiểu con của Object, cho đối tượng thứ hai. Do đó, cả hai phương pháp đều được áp dụng trong giai đoạn thứ hai.

Vì có thể áp dụng nhiều phương pháp, trình biên dịch phải xác định cụ thể nhất là [§15.12.2.5]. Nó so sánh các kiểu tham số, không phải kiểu đối số, và nó không tự động tạo chúng. Đối tượng và boolean là các loại không liên quan, vì vậy chúng được coi là cụ thể như nhau. Cả hai phương pháp đều không cụ thể hơn phương pháp khác, do đó, phương thức gọi là mơ hồ.

Một cách để giải quyết sự mơ hồ là thay đổi tham số boolean thành kiểu Boolean, là một kiểu con của Object.Quá tải đầu tiên sẽ luôn luôn cụ thể hơn (nếu có) so với lần thứ hai.

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