一个Java方法能使用多少个参数?

开发 后端
我不想通过我薄弱的C++技能来测试源代码,所以我决定直接来测试编译器2。我写了一个Python脚本,通过二分法找到一个会触发错误的最小值。完整的代码请见连接Github Repo。

 [[336266]]

我最近给我fork的项目QuickTheories增加了一个接口:

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

这让非常好奇一个方法能够有多少个类型参数呢?据我所知,Java的语言规范并没有提到这个问题。1

关于在实现上这个阈值的定义,我有两个猜测:

编译器会强制一个可预测的阈值,例如255或者65535。

由于实现细节的原因,编译器的异常处理会施加意想不到的限制。

我不想通过我薄弱的C++技能来测试源代码,所以我决定直接来测试编译器2。我写了一个Python脚本,通过二分法找到一个会触发错误的最小值。完整的代码请见连接Github Repo。

最直接的办法就是生成方法。幸运的是,我们不必使用任何已有的类型参数,只需要按照

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

运行这个二分法的代码会有如下输出:

  1. >>> error: UTF8 representation for string "<A1:Ljava/lang/Objec..." is too long for the constant pool  
  2. >>> 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的继承,并将它们编入方法签名内。我通过修改我的生成器来优化这个问题。

循环的关键代码修改为:

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

除开一个实例之外,所有的类型参数都从继承java/lang/Object改为继承A。这个修改将类型参数的数量提升到9851个。

类型参数的数量提升了非常多,而我所使用的编码方法还可以继续改进。例如使用非ASCII unicode标识符,不过我已经比较满意现在的效果了。

这些都不重要

在实际情况中是不太可能达到上述数量限制的。代码生成时可能会达到语言或者编译器的某些极限,就算罕见的遇到了生成上百个类型参数的情况,那距离几千个的限制仍然还相距很远。

尽管如此,如果我是规则的制定者,我将不允许任何类或者方法使用超过255个类型参数的情况。即使只影响了百万分之一的程序,有明确的限制会更好。

  1. §4.4, §8.1.2, §9.1.2, §8.4.4, §8.8.4 这些章节都和方法或者类的类型参数有关,但是都没有指明允许有多少个类型参数。
  2. 当我写这段话时,我想起了Hotspot是C++写的,javac是Java写的。就算这样我依然会选择做代码实验,而不是阅读代码。阅读别人代码是种煎熬
  3. 逗号之后的空格不会影响,因为编译器会规范化它的输出。
  4. 这也表示与我使用哪个JVM无关。为了完整性,我在Fedora 29上使用了1.8.0_191-b13版本的OpenJdk。

本文作者:justinblank, 翻译:Lephix

原文链接:

https://justinblank.com/experiments/howmanytypeparameterscanajavamethodhave.html

版权归作者所有,转载请注明作者、原文、译者等出处信息

 

责任编辑:武晓燕 来源: 51CTO专栏
相关推荐

2023-09-26 16:44:14

光模块

2023-09-04 08:08:59

2022-03-08 22:21:55

网络包队列网卡

2019-12-20 09:31:23

TCPHTTP浏览器

2019-05-29 15:17:43

TCPHTTPSSL

2019-07-09 06:13:09

TCPHTTP网络协议

2020-11-11 10:10:20

调用函数参数变量

2019-01-08 09:23:16

Java字符串编码

2021-03-29 08:47:24

线程面试官线程池

2019-11-14 16:05:29

TCPHTTP前端

2019-01-02 16:31:33

程序员技术互联网

2020-06-16 11:00:40

线程Java代码

2019-09-30 08:50:51

Linux发行版内核

2018-03-09 12:14:36

Linux服务器负载

2024-11-06 08:49:46

2021-03-11 08:32:58

参数模式构造

2020-12-28 05:54:37

构造builder模式

2023-08-28 07:26:01

2024-03-18 08:21:06

TCPUDP协议

2023-06-25 10:04:50

自动驾驶智能
点赞
收藏

51CTO技术栈公众号