Phương thức nạp chồng được chọn như thế nào khi một tham số là giá trị rỗng theo nghĩa đen?


98

Tôi đã gặp câu hỏi này trong một bài kiểm tra,

public class MoneyCalc {

   public void method(Object o) {
      System.out.println("Object Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

Đầu ra của chương trình này là "Phiên bản chuỗi". Nhưng tôi không thể hiểu tại sao việc truyền null cho một phương thức được nạp chồng lại chọn phiên bản chuỗi. Giá trị null có phải là một biến Chuỗi trỏ tới không?

Tuy nhiên, khi mã được thay đổi thành,

public class MoneyCalc {

   public void method(StringBuffer sb) {
      System.out.println("StringBuffer Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

nó đưa ra lỗi biên dịch nói rằng "Phương thức method (StringBuffer) không rõ ràng đối với loại MoneyCalc"


Bạn có thể gán một chuỗi cho giá trị null để nó hợp lệ và thứ tự cho java và hầu hết các ngôn ngữ lập trình phù hợp với kiểu gần nhất và sau đó là đối tượng.
JonH


6
Rõ ràng điều này đã bị đóng lại là trùng lặp bởi những người chỉ đọc tiêu đề. Câu hỏi thực tế ở đây là về lý do tại sao một quá tải cụ thể được chọn, không phải "null là gì".
giữa các

Câu trả lời:


102

Giá trị null có phải là một biến Chuỗi trỏ tới không?

Một tham chiếu rỗng có thể được chuyển đổi thành một biểu thức của bất kỳ loại lớp nào. Vì vậy, trong trường hợp của String, điều này là tốt:

String x = null;

Quá Stringtải ở đây được chọn vì trình biên dịch Java chọn quá tải cụ thể nhất , theo phần 15.12.2.5 của JLS . Đặc biệt:

Trực giác không chính thức là một phương thức cụ thể hơn một phương thức khác nếu bất kỳ lệnh gọi nào được xử lý bởi phương thức đầu tiên có thể được chuyển cho phương thức kia mà không có lỗi kiểu thời gian biên dịch.

Trong trường hợp thứ hai của bạn, cả hai phương pháp vẫn có thể áp dụng, nhưng không Stringvà cũng không StringBuffercụ thể hơn phương pháp kia, do đó không phương pháp nào cụ thể hơn phương thức kia, do đó lỗi trình biên dịch.


3
Và quá tải chuỗi cụ thể hơn quá tải đối tượng như thế nào?
user1610015

12
Bởi vì một Objectcó thể lấy bất kỳ loại nào và bọc nó trong một Object, trong khi một Stringcó thể chỉ lấy a String. Trong trường hợp này, Stringcụ thể hơn của một loại so với Objectloại.
JonH

10
@zakSyed Nếu bạn được hỏi "Chuỗi" hoặc "Đối tượng" chuyên biệt hơn, bạn sẽ nói gì? Rõ ràng là "Chuỗi", phải không? Nếu bạn được hỏi: "String" hay "StringBuffer" chuyên biệt hơn là gì? Không có câu trả lời, cả hai đều là chuyên môn trực giao, làm thế nào bạn có thể chọn giữa chúng? Sau đó, bạn phải rõ ràng về cái nào bạn muốn (ví dụ bằng cách đúc tham chiếu null của bạn question.method((String)null))
Edwin Dalorzo

1
@JonSkeet: Có lý. Vì vậy, về cơ bản nó tìm kiếm một phương pháp theo quy tắc cụ thể nhất và nếu nó không thể quyết định phương thức nào cụ thể hơn thì nó sẽ gây ra lỗi thời gian biên dịch.
zakSyed

3
@JonH "null" là kiểu tham chiếu, nếu một trong các phương thức nhận kiểu nguyên thủy làm tham số (tức là int) thì trình biên dịch thậm chí sẽ không xem xét đến khi chọn phương thức phù hợp để gọi tham chiếu kiểu null. Thật là khó hiểu nếu bởi "Int", bạn có nghĩa là java.lang.Integer hoặc nếu bạn có nghĩa là kiểu nguyên thủy int.
Edwin Dalorzo

9

Ngoài ra, JLS 3.10.7 cũng tuyên bố rằng "null" là một giá trị theo nghĩa đen của "kiểu null". Do đó tồn tại một kiểu gọi là "null".

Sau đó, JLS 4.1 nói rằng tồn tại một kiểu null không thể khai báo các biến, nhưng bạn chỉ có thể sử dụng nó thông qua chữ null. Sau đó nó nói:

Tham chiếu null luôn có thể trải qua chuyển đổi tham chiếu mở rộng thành bất kỳ loại tham chiếu nào.

Tại sao trình biên dịch chọn mở rộng nó thành Chuỗi có thể được giải thích trong câu trả lời của Jon .


Tôi khá chắc rằng loại đó là Void.
xavierm02

2
@ xavierm02 Không có đề cập đến điều đó trong Đặc tả ngôn ngữ Java. Bạn có thể trích dẫn tài liệu tham khảo của mình để tất cả chúng tôi có thể xác minh khiếu nại của bạn không?
Edwin Dalorzo 23/10/12

Tôi chưa bao giờ đọc điều đó. Nhưng tôi đã làm một số Java vào mùa hè này và bạn có thể quá tải một phương thức lấy một Đối tượng bằng một phương thức lấy một đối tượng Void.
xavierm02

Nó thuộc về một lớp học hơn là một kiểu.
xavierm02

4
@ xavierm02 Tất nhiên bạn có thể. Bạn có thể nạp chồng một phương thức trong Java với bất kỳ kiểu nào khác mà bạn muốn. Tuy nhiên, điều đó không liên quan gì đến câu hỏi hay câu trả lời của tôi.
Edwin Dalorzo 23/10/12

2

Bạn có thể gán a stringcho một nullgiá trị để nó hợp lệ và thứ tự cho java và hầu hết các ngôn ngữ lập trình phù hợp với kiểu gần nhất và sau đó là đối tượng.


2

Để trả lời câu hỏi trong tiêu đề: nullkhông phải là a Stringcũng không phải Objectlà tham chiếu đến có thể được gán cho null.

Tôi thực sự ngạc nhiên khi mã này thậm chí còn được biên dịch. Tôi đã thử một cái gì đó tương tự trước đây và tôi gặp lỗi trình biên dịch nói rằng cuộc gọi không rõ ràng.

Tuy nhiên, trong trường hợp này, có vẻ như trình biên dịch đang chọn phương pháp thấp nhất trên chuỗi thức ăn. Nó giả định rằng bạn muốn phiên bản ít chung chung nhất của phương pháp để giúp bạn.

Tôi sẽ phải xem liệu tôi có thể tìm ra ví dụ mà tôi gặp lỗi trình biên dịch trong kịch bản (dường như) chính xác này hay không, mặc dù ...]

CHỈNH SỬA: Tôi hiểu rồi. Trong phiên bản tôi đã tạo, tôi có hai phương thức quá tải chấp nhận a Stringvà an Integer. Trong trường hợp này, không có tham số "cụ thể nhất" (như trong ObjectString), vì vậy nó không thể chọn giữa chúng, không giống như trong mã của bạn.

Câu hỏi rất hay!


null không nhưng nó có thể được gán cho cả hai, đó là sự khác biệt và nó sẽ biên dịch tại sao lại không - đó là mã hoàn toàn hợp lệ.
JonH

0

Vì kiểu Chuỗi cụ thể hơn kiểu Đối tượng. Giả sử bạn thêm một phương thức nữa có kiểu Số nguyên.

public void method(Integer i) {
      System.out.println("Integer Version");
   }

Sau đó, bạn sẽ gặp lỗi trình biên dịch nói rằng cuộc gọi không rõ ràng. Như bây giờ chúng ta có hai phương pháp cụ thể như nhau với cùng mức độ ưu tiên.


0

Trình biên dịch Java cung cấp hầu hết kiểu lớp dẫn xuất để gán null.

Đây là ví dụ để hiểu nó:

class A{

    public void methodA(){
        System.out.println("Hello methodA");
    }
}

class B extends A{
    public void methodB(){
        System.out.println("Hello methodB");
    }
}

class C{
    public void methodC(){
        System.out.println("Hello methodC");
    }
}

public class MyTest {

     public static void fun(B Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

đầu ra: B Class.

Mặt khác:

public class MyTest {

     public static void fun(C Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

Kết quả: Phương thức fun (C) không rõ ràng đối với loại MyTest

Hy vọng nó sẽ giúp hiểu rõ hơn về trường hợp này.


0

Nguồn: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Khái niệm: Phương thức cụ thể nhất
Giải thích: Nếu có nhiều phương thức thành viên thì cả hai đều có thể truy cập và áp dụng cho một lệnh gọi phương thức, cần phải chọn một lệnh để cung cấp bộ mô tả cho việc gửi phương thức thời gian chạy. Ngôn ngữ lập trình Java sử dụng quy tắc mà phương pháp cụ thể nhất được chọn. Hãy thử truyền null sang kiểu cụ thể và phương thức bạn muốn sẽ được tự động gọi.


Trong ví dụ đầu tiên, String mở rộng Đối tượng, vì vậy "cụ thể nhất" là phương thức lấy String, nhưng trong ví dụ thứ hai, String và StringBuffer đều mở rộng Đối tượng, vì vậy chúng đều cụ thể như nhau và do đó trình biên dịch không thể đưa ra lựa chọn
David Kerr

-1

Tôi sẽ không nói. NULL là một trạng thái không phải là một giá trị. Kiểm tra liên kết này để biết thêm thông tin về điều này (bài viết áp dụng cho SQL, nhưng tôi nghĩ nó cũng giúp ích cho câu hỏi của bạn).


nulllà một giá trị rất chắc chắn, như được định nghĩa trong JLS.
Sotirios Delimanolis
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.