一個Java方法能有多少個參數類型?這個好奇coder作了個實驗

在 JVM 中,一個 Java 方法,最多能定義多少參數呢?這是一個很無聊的問題,即便能定義一萬個,十萬個,誰又會真的去這麼作呢。可是做爲一個 coder,最重要的不就是好奇心嗎,沒有好奇心,和一條鹹魚又有什麼區別呢?本文做者就是這樣一位充滿好奇心的 coder。

選自 justinblank,機器之心編譯,參與:李志偉、張倩。
html

我最近給個人 QuickTheories 分支添加了一個接口:java

@FunctionalInterfacepublic interface QuadFunction<A, B, C, D, E> {    E apply(A a, B b, C c, D d);}複製代碼

讓我好奇的是這個方法能有多少個類型參數。到目前爲止,我敢說,Java 語言規範並無談及這個問題。git

對於實現定義的限制多是什麼,我有兩個猜想:github

  1. 編譯器會設置一個可預測的限制,如 255 或 65535。bash

  2. 編譯器的緊急行爲會因爲實現細節(堆棧溢出或一樣不可預測/不相關的東西)而設置意外的限制。app

我不想在源代碼上測試我那點可憐的 C++技巧,因此我決定只測試編譯器作了什麼。我寫了一個 Python 腳本,它使用二進制搜索找到最少的致錯類型參數。完整的腳本放在 Github repo (github.com/hyperpape/j…) 中。測試

腳本地址:github.com/hyperpape/j…ui

生成方法很簡單。幸運的是,咱們沒必要使用任何類型參數,只需以<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("}")複製代碼

運行二進制搜索能夠獲得如下輸出:spa

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

這個錯誤有點模糊,但過後看來是能夠預見的。編譯器生成的類文件包含許多字符串,包括類中每一個方法的方法簽名。這些字符串存儲在常量池中,常量池中的條目最大爲 65535 字節,這是由 JVM 規範規定的限制。

因此,我以前的猜想都不徹底正確。類型參數的最大數目是一個突現特徵(emergent property),而不是一個明確的決定。不過,並非編譯器自己的實現致使了錯誤。

相反,JVM 的類文件格式限制了能夠在類文件中表示的類型參數的數量。這是真的,儘管 JVM 對泛型一無所知。這也意味着類型參數的最大數目徹底取決於如何編寫方法。

我嘗試了一種新的編碼類型參數的方法(先前連接文件中的 write_Type_Compact),使用完整的合法 ASCII 字符(A-Z、a-z、$和_)。該實現有點過於複雜,由於可使用字符 0~9,但不能是標識符的初始字符,由於 Java 關鍵字不能做爲類型參數出現。我只是用等長的 UTF-8 字符替換了短單詞「if」和「do」。更緊湊的編碼將參數數量從 2776 增長到 3123。

不方便的是,_A 是一種合法的 Java 標識符,但 _ 不是。謝天謝地,個人編碼在不使用初始_狀況下就生成了 3392 個 2 字節類型參數,所以我以爲沒有必要進行簿記以發出初始字符_。

再來一個小技巧

解壓類文件顯示,65536 個字符的大部分不是我生成的類型參數,而是子字符串 Ljava/lang/object 的重複實例。由於沒有提供關於類型參數的信息,因此類文件顯示它們擴展了對象,並在方法簽名中對其進行編碼。我修改了生成器來解決這個問題。

循環的關鍵部分是:

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

在類型參數中,除了一個實例 java/Lang/Object 以外的全部實例都被替換爲 A。在進行了這個更改以後,編譯了一個具備 9851 個類型參數的方法。

因爲參數的數量增長了不少,因此我使用的代碼確定須要調整。使用非 ASCII Unicode 標識符多是徹底高效的必要條件,但簡單地指出這是能夠作到的我就很滿意了。

這些都不重要

很難想象有人會達到這個極限。代碼生成有時會達到語言或編譯器的限制,但即便生成的代碼彷佛也不太可能使用成百上千的類型參數。

儘管如此,若是我是規則制定者,我會考慮明確禁止任何類或方法具備 255 個以上的類型參數。明確的限制彷佛更好,即便它隻影響百萬分之一的程序。

原文連接:justinblank.com/experiments…

相關文章
相關標籤/搜索