查看原文
其他

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

点击上方 "程序员小乐"关注公众号, 星标或置顶一起成长

每天早上8点20分, 第一时间与你相约

每日英文

No matter how painful an experience you might have had, eventually everything will be forgotten. There's nothing that can beat time.

不管经历多痛的事情,到最后都会渐渐遗忘。没有什么能敌得过时光。


每日掏心话

信任是把刀,你给了别人,他就有两个选择,捅你或者保护你。看清一个人何必去揭穿;讨厌一个人又何必去翻脸。


来自:机器之心 | 责编:乐乐

程序员小乐(ID:study_tech)第 649 次推文   图片来自网络


往日回顾:Web 登录其实没你想的那么简单!



   正文   


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



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


@FunctionalInterface
public interface QuadFunction<A, B, C, D, E> {
    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


生成方法很简单。幸运的是,我们不必使用任何类型参数,只需以<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 规范规定的限制。


所以,我之前的猜测都不完全正确。类型参数的最大数目是一个突现特征(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


欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

欢迎各位读者加入程序员小乐技术群,在公众号后台回复“加群”或者“学习”即可。

猜你还想看


阿里、腾讯、百度、华为、京东最新面试题汇集

Redis 面试最常被问到21个知识点总结

Java 开发中如何正确的踩坑

如何写一个更好的Python函数?


关注微信公众号「程序员小乐」,收看更多精彩内容
嘿,你在看吗?

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存