引子:大家可以思考一下下面程序的輸出結果
public class TestNull { public void show(String a){ System.out.println("String"); } public void show(Object o){ System.out.println("Object"); } public static void main(String args[]){ TestMain t = new TestMain(); t.show(null); } }
運行的結果是:
String
解釋(主要是重載函數調用時精確性的問題):
《java解惑》這本書謎題46解釋了這種情況。下面內容摘自《Java解惑》
謎題46:令人混淆的構造器案例
本謎題呈現給你了兩個容易令人混淆的構造器。main方法調用了一個構造器,但是它調用的到底是哪一個呢?該程序的輸出取決於這個問題的答案。那么它到底會打印出什么呢?甚至它是否是合法的呢?
public class Confusing { private Confusing(Object o) { System.out.println("Object"); } private Confusing(double[] dArray) { System.out.println("double array"); } public static void main(String[] args) { new Confusing(null); } }
傳遞給構造器的參數是一個空的對象的引用,因此,初看起來,改程序好像應該調用參數類型為Object的重載版本,並且將打印出Object。另一方面,數組也是引用類型,因此Null也可以應用於類型是double[]的重載版本。由此可能會得出結論:
這個調用是模棱兩可的,改程序應該是不能編譯的
如果你試着去運行程序,就會發現我之前的直觀猜測是不對的:該程序打印 的是
double array
這種行為有悖常理,但是有一個很好的理由可以解釋它,
一:選取所有可獲得的並可以應用的構造器或方法
二:在第一步選取的方法或者構造器中選擇最精確的一個 ,二第二個方法就是缺乏精確性
在我們的程序中,兩個構造器都是可獲得 並且可應用的。構造器Confusing(Object)可以接受任何傳遞給Confusing(double[ ])的參數,因此Confusing(Object)相對缺乏精確性。(每一個double數組都是一個Object,但是每一個Object並不一定是 一個double數組。)因此,最精確的構造器就是Confusing(double[ ]),這也就解釋了為什么程序會產生這樣的輸出。
理解本謎題的關鍵在於在測試哪一個方法或構造器最精確時,這些測試沒有使用實際的參數:即出現在調用中的參數。這些參數只是被用來確定哪一個重載版本是可應用的。一旦編譯器確定了哪些重載版本是可獲得且可應用的,它就會選擇最精確的一個重載版本,而此時使用的僅僅是形式參數:即出現在聲明中的參數。
要想用一個null參數來調用Confusing(Object)構造器,你需要這樣寫代碼:
以這種方式來在多個重載版本中進行選擇是相當令人不快的。在你的API 中,應該確保不會讓客戶端走這種極端。理想狀態下,你應該避免使用重載:為不同的方法取不同的名稱。當然,有時候這無法實現,例如,構造器就沒有名稱,因 而也就無法被賦予不同的名稱。然而,你可以通過將構造器設置為私有的並提供公有的靜態工廠,以此來緩解這個問題[EJ Item 1]。如果構造器有許多參數,你可以用Builder模式[Gamma95]來減少對重載版本的需求量。
如果你確實進行了重載,那么請確保所有的重載版本所接受的參數類型都互不兼容,這樣,任何兩個重載版本都不會同時是可應用的。如果做不到這一點,那么就請確保所有可應用的重載版本都具有相同的行為[EJ Item 26]。
總之,重載版本的解析可能會產生混淆。應該盡可能地避免重載,如果你必須進行重載,那么你必須遵守上述方針,以最小化這種混淆。如果一個設計糟糕的API強制你在不同的重載版本之間進行選擇,那么請將實際的參數轉型為你希望調用的重載版本的形式參數所具有的類型。
參考文檔:https://blog.csdn.net/liusrblog/article/details/8088305
