從零開始開發JVM語言(十二)重載方法的選擇

目錄戳這裏java

方法重載在面向對象裏很基礎,不論是學校仍是自學時都會教到這個概念。實際上相互重載的方法除了名字相同以外,並無任何關係,由於它們擁有徹底不一樣的方法簽名。git

JVM的invokeXXX字節碼(除了invokedynamic,其用法前文已寫),都須要一個方法簽名來指定調用哪一個方法。例如github

new java/lang/Object
dup
invokespecial java/lang/Object:<init>()V

先建立一個java.lang.Object對象,而後再棧中複製這個「未初始化的對象」,接着調用構造函數進行初始化。函數

方法簽名由兩部分構成,.net

  1. 方法所在類型,例如 java/lang/Object
  2. 方法的descriptor

然後者又由兩部分組成code

  1. 方法參數,用小括號包圍,例如 (Ljava/lang/String;I)就表明了(java.lang.String, int)
  2. 方法返回值,也是一個類型,例如 Ljava/util/regex/Pattern;

因此說,兩個重載方法之間,對於JVM來講一點關係都沒有,對於使用者(咱們)來講,相關聯的僅僅是名稱相同而已。編譯中要作的事情是根據方法名稱和給定的參數類型‘定位’到要調用的方法。對象

從直觀上就能夠看出,想找到要調用的方法有這幾個因素須要知足:blog

  1. 方法可以被訪問到ip

  2. 方法名稱一致ci

  3. 方法參數長度一致

  4. 每一個參數(parameter)的類型都要可以由變量(argument)轉換而來,例如

    String.valueOf('abc')

    其中'abc'是字符串,就不能匹配到String.valueOf(int)這個方法

  5. (最關鍵的一點)方法應當是最佳的。

什麼是最佳的方法呢?例如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。而intInteger這樣的轉換是「隱式類型轉換」,須要加上一個常數,好比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

相關文章
相關標籤/搜索