一個(gè)Java方法能使用多少個(gè)參數(shù)?

我最近給我fork的項(xiàng)目QuickTheories增加了一個(gè)接口:

@FunctionalInterface
public interface QuadFunction<A, B, C, D, E> {
    E apply(A a, B b, C c, D d);
}

這讓非常好奇一個(gè)方法能夠有多少個(gè)類型參數(shù)呢?據(jù)我所知猾编,Java的語言規(guī)范并沒有提到這個(gè)問題。1

關(guān)于在實(shí)現(xiàn)上這個(gè)閾值的定義升敲,我有兩個(gè)猜測(cè):

  1. 編譯器會(huì)強(qiáng)制一個(gè)可預(yù)測(cè)的閾值答倡,例如255或者65535。
  2. 由于實(shí)現(xiàn)細(xì)節(jié)的原因驴党,編譯器的異常處理會(huì)施加意想不到的限制瘪撇。

我不想通過我薄弱的C++技能來測(cè)試源代碼,所以我決定直接來測(cè)試編譯器2港庄。我寫了一個(gè)Python腳本倔既,通過二分法找到一個(gè)會(huì)觸發(fā)錯(cuò)誤的最小值。完整的代碼請(qǐng)見連接Github Repo鹏氧。

最直接的辦法就是生成方法渤涌。幸運(yùn)的是,我們不必使用任何已有的類型參數(shù)把还,只需要按照<A,B,C..>的形式來生成:

def write_type_plain(count):
    with open('Test.java', 'w') as f:
        f.write("public class Test {\n")
        f.write("public <")
        for i in range(count):
            if (i > 0):
                f.write(", ")
            f.write("A" + str(i + 1))
        f.write("> void testMethod() {}")
        f.write("}")

運(yùn)行這個(gè)二分法的代碼會(huì)有如下輸出:

>>> error: UTF8 representation for string "<A1:Ljava/lang/Objec..." is too long for the constant pool 
>>> largest type: 2776

這個(gè)錯(cuò)誤讓人有點(diǎn)費(fèi)解实蓬,但是從事后來看還是可以理解的稿存。編譯器生成的類文件包含多個(gè)字符串,包括每個(gè)方法的方法簽名瞳秽。這些字符串保存在常量池內(nèi),而常量池的內(nèi)容有最大65535字節(jié)數(shù)的限制率翅,這個(gè)是JVM的所定義的练俐。

所以,我之前的猜測(cè)都不是完全的正確冕臭。類型參數(shù)的最大個(gè)數(shù)是一個(gè)意料之外的值腺晾,而不是一個(gè)確定值。但是辜贵,編譯器的實(shí)現(xiàn)本身并不是導(dǎo)致錯(cuò)誤的原因3悯蝉。相反,是JVM類文件的格式要求限制了類型參數(shù)可使用的數(shù)量托慨。其實(shí)JVM對(duì)泛型本身一無所知鼻由。

這同時(shí)也表示類型參數(shù)的最大個(gè)數(shù)取決于你寫的方法代碼4。我嘗試用另外一種類型參數(shù)的編碼方案(先前鏈接文中的write_type_compact)厚棵,使用全部合法的ASCII字符蕉世。這個(gè)實(shí)現(xiàn)是有點(diǎn)繁瑣的,因?yàn)樽址?-9是合法的婆硬,但不能作為標(biāo)識(shí)符的首字母狠轻,并且Java關(guān)鍵字也不能作為類型參數(shù)。我僅僅將ifdo替換為等長的UTF-8字符彬犯。采用這種更緊湊的編碼方案讓類型參數(shù)的個(gè)數(shù)從2776提升到了3123向楼。

還是有一些不太方便的地方,例如_A是一個(gè)合法的Java標(biāo)識(shí)符谐区,但是_不是湖蜕。我的編碼在不使用_作為首字幕的情況下,最高生成了3392個(gè)2字節(jié)的類型參數(shù)卢佣。所以我覺得不用考慮_作為首字母的情況了重荠。

另外一個(gè)技巧

通過反編譯類文件,我觀察到65536個(gè)字符中大部分都不是我生成的類型參數(shù)虚茶,而是重復(fù)的字符串Ljava/lang/Object;戈鲁。這是因?yàn)轭愋蛥?shù)沒有包含額外的信息,所以類文件將其視為Object的繼承嘹叫,并將它們編入方法簽名內(nèi)婆殿。我通過修改我的生成器來優(yōu)化這個(gè)問題。

循環(huán)的關(guān)鍵代碼修改為:

s = type_var(i)
f.write(s)
if (s != 'A'):
    f.write(" extends A")

除開一個(gè)實(shí)例之外罩扇,所有的類型參數(shù)都從繼承java/lang/Object改為繼承A婆芦。這個(gè)修改將類型參數(shù)的數(shù)量提升到9851個(gè)怕磨。

類型參數(shù)的數(shù)量提升了非常多,而我所使用的編碼方法還可以繼續(xù)改進(jìn)消约。例如使用非ASCII unicode標(biāo)識(shí)符肠鲫,不過我已經(jīng)比較滿意現(xiàn)在的效果了。

這些都不重要

在實(shí)際情況中是不太可能達(dá)到上述數(shù)量限制的或粮。代碼生成時(shí)可能會(huì)達(dá)到語言或者編譯器的某些極限导饲,就算罕見的遇到了生成上百個(gè)類型參數(shù)的情況,那距離幾千個(gè)的限制仍然還相距很遠(yuǎn)氯材。

盡管如此渣锦,如果我是規(guī)則的制定者,我將不允許任何類或者方法使用超過255個(gè)類型參數(shù)的情況氢哮。即使只影響了百萬分之一的程序袋毙,有明確的限制會(huì)更好。

  1. §4.4, §8.1.2, §9.1.2, §8.4.4, §8.8.4 這些章節(jié)都和方法或者類的類型參數(shù)有關(guān)冗尤,但是都沒有指明允許有多少個(gè)類型參數(shù)听盖。
  2. 當(dāng)我寫這段話時(shí),我想起了Hotspot是C++寫的裂七,javac是Java寫的媳溺。就算這樣我依然會(huì)選擇做代碼實(shí)驗(yàn),而不是閱讀代碼碍讯。閱讀別人代碼是種煎熬
  3. 逗號(hào)之后的空格不會(huì)影響悬蔽,因?yàn)榫幾g器會(huì)規(guī)范化它的輸出。
  4. 這也表示與我使用哪個(gè)JVM無關(guān)捉兴。為了完整性蝎困,我在Fedora 29上使用了1.8.0_191-b13版本的OpenJdk。

本文作者:justinblank倍啥, 翻譯:1 Way
原文鏈接:https://justinblank.com/experiments/howmanytypeparameterscanajavamethodhave.html
譯文首發(fā):http://blog.didispace.com/howmanytypeparameterscanajavamethodhave/

本文有spring4all技術(shù)翻譯組完成禾乘,更多國外前沿知識(shí)和干貨好文,歡迎關(guān)注公眾號(hào):后端面試那些事兒虽缕。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末始藕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子氮趋,更是在濱河造成了極大的恐慌伍派,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剩胁,死亡現(xiàn)場(chǎng)離奇詭異诉植,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)昵观,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門晾腔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舌稀,“玉大人,你說我怎么就攤上這事灼擂”诓椋” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵剔应,是天一觀的道長潮罪。 經(jīng)常有香客問我,道長领斥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任沃暗,我火速辦了婚禮月洛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘孽锥。我一直安慰自己嚼黔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布惜辑。 她就那樣靜靜地躺著唬涧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盛撑。 梳的紋絲不亂的頭發(fā)上碎节,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音抵卫,去河邊找鬼狮荔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛介粘,可吹牛的內(nèi)容都是我干的殖氏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼姻采,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼雅采!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慨亲,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤婚瓜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后刑棵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闰渔,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年铐望,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冈涧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茂附。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖督弓,靈堂內(nèi)的尸體忽然破棺而出营曼,到底是詐尸還是另有隱情,我是刑警寧澤愚隧,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布蒂阱,位于F島的核電站,受9級(jí)特大地震影響狂塘,放射性物質(zhì)發(fā)生泄漏录煤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一荞胡、第九天 我趴在偏房一處隱蔽的房頂上張望妈踊。 院中可真熱鬧,春花似錦泪漂、人聲如沸廊营。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽露筒。三九已至,卻和暖如春敌卓,著一層夾襖步出監(jiān)牢的瞬間慎式,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國打工趟径, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞬捕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓舵抹,卻偏偏與公主長得像肪虎,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惧蛹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355