一个Java方法能有多少个参数类型?这个好奇coder做了个实验

百家 作者:机器之心 2019-06-11 16:00:53

选自?justinblank

机器之心编译

参与:李志伟、张倩

在 JVM 中,一个 Java 方法,最多能定义多少参数呢?这是一个很无聊的问题,即使能定义一万个,十万个,谁又会真的去这么做呢。但是作为一个 coder,最重要的不就是好奇心吗,没有好奇心,和一条咸鱼又有什么区别呢?本文作者就是这样一位充满好奇心的 coder。



我最近给我的 QuickTheories 分支添加了一个接口:


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


让我好奇的是这个方法能有多少个类型参数。到目前为止,我敢说,Java 语言规范并没有谈及这个问题。


对于实现定义的限制可能是什么,我有两个猜测:


  1. 编译器会设置一个可预测的限制,如 255 或 65535。

  2. 编译器的紧急行为会由于实现细节(堆栈溢出或同样不可预测/不相关的东西)而设置意外的限制。


我不想在源代码上测试我那点可怜的 C++技巧,所以我决定只测试编译器做了什么。我写了一个 Python 脚本,它使用二进制搜索找到最少的致错类型参数。完整的脚本放在 Github repo (https://github.com/hyperpape/java-max-type-params) 中。


脚本地址:https://github.com/hyperpape/java-max-type-params


生成方法很简单。幸运的是,我们不必使用任何类型参数,只需以的形式发出它们:


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?"?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 个以上的类型参数。明确的限制似乎更好,即使它只影响百万分之一的程序。


原文链接:http://justinblank.com/experiments/howmanytypeparameterscanajavamethodhave.html



本文为机器之心编译,转载请联系本公众号获得授权

?------------------------------------------------

加入机器之心(全职记者 / 实习生):hr@jiqizhixin.com

投稿或寻求报道:content@jiqizhixin.c

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接