目錄戳這裏java
方法重載在面向對象裏很基礎,不論是學校仍是自學時都會教到這個概念。實際上相互重載的方法除了名字相同以外,並無任何關係,由於它們擁有徹底不一樣的方法簽名。git
JVM的invokeXXX
字節碼(除了invokedynamic
,其用法前文已寫),都須要一個方法簽名來指定調用哪一個方法。例如github
new java/lang/Object dup invokespecial java/lang/Object:<init>()V
先建立一個java.lang.Object
對象,而後再棧中複製這個「未初始化的對象」,接着調用構造函數進行初始化。函數
方法簽名由兩部分構成,.net
java/lang/Object
descriptor
然後者又由兩部分組成code
(Ljava/lang/String;I)
就表明了(java.lang.String, int)
Ljava/util/regex/Pattern;
因此說,兩個重載方法之間,對於JVM來講一點關係都沒有,對於使用者(咱們)來講,相關聯的僅僅是名稱相同而已。編譯中要作的事情是根據方法名稱和給定的參數類型‘定位’到要調用的方法。對象
從直觀上就能夠看出,想找到要調用的方法有這幾個因素須要知足:blog
方法可以被訪問到ip
方法名稱一致ci
方法參數長度一致
每一個參數(parameter)的類型都要可以由變量(argument)轉換而來,例如
String.valueOf('abc')
其中'abc'
是字符串,就不能匹配到String.valueOf(int)
這個方法
(最關鍵的一點)方法應當是最佳的。
什麼是最佳的方法呢?例如java.util.List
有這兩個方法
remove(int) remove(Object)
咱們都知道,若是給出int
型,就會調用前者,若是給出引用類型,例如java.lang.Integer
,就會調用後者。比方說:
list = [1,2,3] list.remove(1)
最後結果是[1,3]
。若是是
list.remove(Integer(1))
最後結果是[2,3]
。
這個例子是基本類型和引用類型間的,它們區別比較大,再看一個同爲引用類型的例子:
class List concat(o : Object) : List concat(list : List) : List
上述定義的concat
方法,一個接收Object
類型,一個接收List
類型。在調用時,若是給出list.concat('abc')
,就須要調用Object
的重載,若是給出list.concat([1,2,3])
就須要調用List
的重載。
固然,若是把List向上轉換爲Object
再來調用,好比list.concat([1,2,3] as Object)
會調用Object
的重載,由於編譯器只管給定的類型,不會無論(有的時候也不可能知道)確切的類型。
在編譯時(動態類型語言在運行時也)要保證調用的方法「最佳」。我定義的「最佳」是: 在轉換到目標類型所需的「距離」最短。
這裏的「距離」在以前 step 3 檢查合法性時就提到過了。向上轉型一次,距離就+1。而int
到Integer
這樣的轉換是「隱式類型轉換」,須要加上一個常數,好比10000
,一個比較大的數字,保證正常的向上轉型永遠不會大於隱式轉換,也即隱式轉換優先級更低。
最終能夠獲得一些這樣的「元組」,表示每一個參數的轉換次數
例如:方法爲(String, Object)
和(CharSequence, Object)
參數和對應元組:
(String, List) -- (0, 1)/(1, 1)
這樣一來,前者每一個數字都不大於後者,因此應當調用前者。
可是若是有這種狀況方法爲(String, Object)
和(CharSequence, List)
(String, List) -- (0, 1)/(1, 0)
那就沒法肯定了,由於前._0
< 後._0
可是 前._1
> 後._1
。Java也是如此,若是有興趣能夠嘗試一下。
void a(CharSequence c, List list){} void a(String c, Object list){} a("a",new ArrayList());
會報錯:兩個方法都匹配。因此有的地方纔會在方法調用參數上加一個類型。
那麼若是出現須要「隱式轉換」的狀況呢?只要加上一個比較大的常數便可。實際沒什麼太大區別。
放個代碼:編譯期的方法匹配戳這裏。編譯期沒有徹底的使用本篇的距離,而是直接判斷「好壞」. 運行時的方法尋找戳這裏,這和本篇說的方式如出一轍,由於有隱式類型轉換的存在,因此不得不加個距離。
最後,但願看官可以關注個人編譯器哦~Latte