簡體   English   中英

Java:靜態完成方法名稱/簽名解析(編譯時)?

[英]Java: Is method name/signature resolution done statically (compile-time)?

我今天遇到了一個有趣的問題,我認為這在Java中是不可能的。 我針對jgroups的2.6版本編譯了我的java代碼,但是在運行時使用了版本2.12(tomcat web app部署)。 我收到以下錯誤

org.jgroups.Message.<init>(Lorg/jgroups/Address;Lorg/jgroups/Address;Ljava/io/Serializable;)

假設從那時起API會發生變化,我想把我的代碼移植到jgroups-2.12,但令我驚訝的是用jgroups-2.12編譯好的代碼,當我更換新jar時(在我的代碼中沒有改變一行,只是編譯jgroups-2.12而不是jgroups-2.6),它工作得非常好。

后來我意識到2.6中的構造函數Message(Address, Address, Serializable) )在2.12中被改為Message(Address,Address,Object)。 這意味着在運行時,JVM試圖找到完全相同的方法,但未能這樣做。

這是否意味着Java編譯器在編譯時嵌入了確切的方法名稱和精確的參數,而具有更廣泛參數的方法將不起作用?

是的,這是完全正確的 - 確切的簽名在編譯時被綁定,這就是字節碼中包含的內容。

實際上,這甚至包括返回類型,它不包含在重載等目的的簽名中。

從根本上說,如果您更改現有公共API成員的任何內容,那將是一個重大變化。 您可以通過一些僅限語言的更改來逃避,例如將String[]參數更改為String...參數,或者引入泛型(在某些情況下,如果擦除與前面的代碼兼容),但這幾乎是它。

Java語言規范的第13章是關於二進制兼容性的 - 閱讀它以獲取更多信息。 但特別是,從第13.4.14節

更改方法或構造函數的形式參數的名稱不會影響預先存在的二進制文件。 更改方法的名稱,方法或構造函數的形式參數的類型,或者從方法或構造函數聲明添加參數或從參數或構造函數聲明中刪除參數會創建具有新簽名的方法或構造函數,並具有以下組合的效果:使用舊簽名刪除方法或構造函數,並使用新簽名添加方法或構造函數(參見§13.4.12)。

這是否意味着Java編譯器在編譯時嵌入了確切的方法名稱和精確的參數,而具有更廣泛參數的方法將不起作用?

究竟。 您還可以從您收到的錯誤消息中看到此信息:

org.jgroups.Message.<init>(Lorg/jgroups/Address;Lorg/jgroups/Address;Ljava/io/Serializable;)

完整的簽名包含在這里,運行時尋找完美的匹配。

在更改API時,還有其他幾種情況會破壞Java中的二進制兼容性,但不會破壞源兼容性,例如,當您將基元類型更改為其盒裝變體時,反之亦然。 正如Jon指出的那樣,只有Generics中的更改(但不是所有更改)和使用VarArgs語法都不會影響運行時方法的解析,因為兩者都只是編譯器功能而不會影響字節碼。

這也意味着當您在新庫版本中引入方法的重載時,此重載將僅由使用新版本編譯的調用方使用。 舊的二進制文件仍將調用舊方法,即使它們的參數類型更適合新的重載。

因此,對於庫設計者來說,有時建議不要更改現有方法的簽名,而只是添加新的重載(並將舊方法轉發給新方法,以便調用哪個方法無關緊要)。 當然缺點是所有這些重載都會掩蓋真正的API,並使理解API變得更加困難。

Java編譯器必須在編譯文件中放入確切的方法名稱和精確參數,以便稍后確定要加載哪個類以及調用哪個方法。 除此之外,沒有辦法精確調用所請求的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM