共计 2090 个字符,预计需要花费 6 分钟才能阅读完成。
我最近给我 fork 的我的项目 QuickTheories 减少了一个接口:
@FunctionalInterface
public interface QuadFunction<A, B, C, D, E> {E apply(A a, B b, C c, D d);
}
这让十分好奇一个办法可能有多少个类型参数呢?据我所知,Java 的语言标准并没有提到这个问题。1
对于在实现上这个阈值的定义,我有两个猜想:
- 编译器会强制一个可预测的阈值,例如 255 或者 65535。
- 因为实现细节的起因,编译器的异样解决会施加意想不到的限度。
我不想通过我单薄的 C ++ 技能来测试源代码,所以我决定间接来测试编译器 2。我写了一个 Python 脚本,通过二分法找到一个会触发谬误的最小值。残缺的代码请见连贯 Github Repo。
最间接的方法就是生成办法。侥幸的是,咱们不用应用任何已有的类型参数,只须要依照 <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("}")
运行这个二分法的代码会有如下输入:
>>> error: UTF8 representation for string "<A1:Ljava/lang/Objec..." is too long for the constant pool
>>> largest type: 2776
这个谬误让人有点费解,然而从事起初看还是能够了解的。编译器生成的类文件蕴含多个字符串,包含每个办法的办法签名。这些字符串保留在常量池内,而常量池的内容有最大 65535 字节数的限度,这个是 JVM 的所定义的。
所以,我之前的猜想都不是齐全的正确。类型参数的最大个数是一个意料之外的值,而不是一个确定值。然而,编译器的实现自身并不是导致谬误的起因 3。相同,是 JVM 类文件的格局要求限度了类型参数可应用的数量。其实 JVM 对泛型自身无所不知。
这同时也示意类型参数的最大个数取决于你写的办法代码 4。我尝试用另外一种类型参数的编码方案(先前链接文中的 write_type_compact
),应用全副非法的 ASCII 字符。这个实现是有点繁琐的,因为字符 0 - 9 是非法的,但不能作为标识符的首字母,并且 Java 关键字也不能作为类型参数。我仅仅将if
和do
替换为等长的 UTF- 8 字符。采纳这种更紧凑的编码方案让类型参数的个数从 2776 晋升到了 3123。
还是有一些不太不便的中央,例如 _A
是一个非法的 Java 标识符,然而 _
不是。我的编码在不应用 _
作为首字幕的状况下,最高生成了 3392 个 2 字节的类型参数。所以我感觉不必思考 _
作为首字母的状况了。
另外一个技巧
通过反编译类文件,我察看到 65536 个字符中大部分都不是我生成的类型参数,而是反复的字符串 Ljava/lang/Object;
。这是因为类型参数没有蕴含额定的信息,所以类文件将其视为Object
的继承,并将它们编入办法签名内。我通过批改我的生成器来优化这个问题。
循环的要害代码批改为:
s = type_var(i)
f.write(s)
if (s != 'A'):
f.write("extends A")
除开一个实例之外,所有的类型参数都从继承 java/lang/Object
改为继承A
。这个批改将类型参数的数量晋升到 9851 个。
类型参数的数量晋升了十分多,而我所应用的编码方法还能够持续改良。例如应用非 ASCII unicode 标识符,不过我曾经比较满意当初的成果了。
这些都不重要
在理论状况中是不太可能达到上述数量限度的。代码生成时可能会达到语言或者编译器的某些极限,就算常见的遇到了生成上百个类型参数的状况,那间隔几千个的限度依然还相距很远。
尽管如此,如果我是规定的制定者,我将不容许任何类或者办法应用超过 255 个类型参数的状况。即便只影响了百万分之一的程序,有明确的限度会更好。
- §4.4, §8.1.2, §9.1.2, §8.4.4, §8.8.4 这些章节都和办法或者类的类型参数无关,然而都没有指明容许有多少个类型参数。
- 当我写这段话时,我想起了 Hotspot 是 C ++ 写的,javac 是 Java 写的。就算这样我仍然会抉择做代码试验,而不是浏览代码。浏览他人代码是种煎熬
- 逗号之后的空格不会影响,因为编译器会规范化它的输入。
- 这也示意与我应用哪个 JVM 无关。为了完整性,我在 Fedora 29 上应用了 1.8.0_191-b13 版本的 OpenJdk。
本文作者:justinblank,翻译:1 Way
原文链接:https://justinblank.com/exper…
译文首发:http://blog.didispace.com/how…
本文有 spring4all 技术翻译组实现,更多国外前沿常识和干货好文,欢送关注公众号:后端面试那些事儿。