Java 嵌套双功能的深度(或限制,如果有)是多少

Java 嵌套双功能的深度(或限制,如果有)是多少,java,functional-programming,java-8,Java,Functional Programming,Java 8,我一直在玩BiFunction(java.util.function)。我举了一些例子,我有一个问题 该操作的嵌套次数是否有限制 有双功能?它是否像嵌套一个假设的add(a,b) 想用多少次就用多少次 e、 g.三个嵌套的theFunction.apply() 不停地。。。嵌套的数量可以不断增加,我用 嵌套函数超过十次 我没有确切的要求,有多少嵌套是需要的。。。但我很好奇这样的嵌套可以做多少?我不知道这有一个内在的表达式级别限制,但实际上它会受到JVM方法大小限制65534字节的限制: end_

我一直在玩
BiFunction
java.util.function
)。我举了一些例子,我有一个问题

该操作的嵌套次数是否有限制 有双功能?它是否像嵌套一个假设的
add(a,b)
想用多少次就用多少次

e、 g.三个嵌套的
theFunction.apply()

不停地。。。嵌套的数量可以不断增加,我用 嵌套函数超过十次


我没有确切的要求,有多少嵌套是需要的。。。但我很好奇这样的嵌套可以做多少?

我不知道这有一个内在的表达式级别限制,但实际上它会受到JVM方法大小限制65534字节的限制:

end_pc是独占的这一事实是Java虚拟机设计中的一个历史错误:如果一个方法的Java虚拟机代码的长度正好是65535字节,并且以一条1字节长的指令结束,那么该指令就不能受到异常处理程序的保护。编译器编写器可以通过将任何方法、实例初始化方法或静态初始值设定项(任何代码数组的大小)生成的Java虚拟机代码的最大大小限制为65534字节来解决此错误


来源:

首先,这并不是特定于
BiFunction
的。因此,您基本上是在问,可以嵌套多深的方法调用,简单的答案是Java编程语言本身没有指定限制

技术限制可能会限制数量,但这些是技术限制,而不是规格限制。当技术发展而规范没有变化时,它们可能会被取消

例如,如果异常处理程序应该覆盖最后一条指令,则方法的代码大小限制为65535字节或65534字节。此代码大小支持的嵌套方法调用的数量取决于某些因素。例如,您使用的是
接口
,接口方法调用比具体类方法调用(invokevirtual指令)使用的字节更多,您使用的是
BiFunction
而不是直接的
IntBinaryOperator
,因此每次调用都涉及到
int
值的装箱,这需要额外的代码

但无论如何,还有另一个技术限制,编译器实现。当试图用更高的嵌套计数编译示例时,
javac
从命令行运行,在1500次嵌套调用时以stackoverflow终止,而Netbeans(使用与
javac
相同的编译器代码)在IDE开始表现出奇怪行为之前编译了2000次嵌套调用(我猜,它不能很好地处理编译器/语法高亮器的堆栈溢出)

这表明IDE具有更高的堆栈大小,或者环境设置中的其他差异影响了解析表达式之前的初始堆栈深度。这导致了一个结论,即在实践中没有硬限制。您可以编写一个编译器编译的代码没有问题,而另一个编译器编译的代码没有问题出去-把这个最大化不是个好主意

毕竟,问题代码的等价物可以写成:

public static int methodContainingMethod(
    int a, int b, int c, BiFunction<Integer, Integer, Integer> theFunction) {

    int value = theFunction.apply(a, b);
    for(int i=0; i<asDeepAsYouWannaGo; i++)
        value=theFunction.apply(value, c);
    return value;
}


我喜欢这个答案。它很好地演示了编译器如何无法编译正确的、但由于实现特定的原因而病态的代码。它通过循环或流API提供的迭代应用程序的替代方案比在源代码中显式嵌套函数应用程序更干净、更易于维护。@AlainO'Dea:我再次更改了数字,因为65534只是解决异常处理程序问题的建议。由于问题的方法不包含异常处理程序,因此理论上它可以使用65535字节。请注意,对于普通Java代码,异常处理程序问题与异常处理程序无关,因为异常处理程序本身并不包含异常处理程序换句话说,您可以通过对代码重新排序来解决此问题,这样一个异常处理程序就不会被另一个异常处理程序覆盖,从而消除了异常处理程序覆盖65535条指令的需要。眼睛好!这很有意义。另外请注意还有另一个限制:运行时堆栈深度。这是一个函数不仅具有配置的堆栈大小,而且调用堆栈上还有其他内容。@Brian Goetz:在这种特殊情况下,运行时堆栈并不重要,因为每次调用都在下一次调用之前完成。它不是递归。除非我们讨论的是编译器的堆栈…
return
  theFunction.apply(theFunction.apply(theFunction.apply(theFunction.apply(a,b),c),c),c),c);
public static int methodContainingMethod(
    int a, int b, int c, BiFunction<Integer, Integer, Integer> theFunction) {

    int value = theFunction.apply(a, b);
    for(int i=0; i<asDeepAsYouWannaGo; i++)
        value=theFunction.apply(value, c);
    return value;
}
public static int methodContainingMethod(
    IntBinaryOperator theFunction, int first, int second, int... rest) {

  int value = theFunction.applyAsInt(first, second);
  for(int next: rest) value=theFunction.applyAsInt(value, next);
  return value;
}
public static OptionalInt methodContainingMethod(
    IntBinaryOperator theFunction, int... arguments) {

  return IntStream.of(arguments).reduce(theFunction);
}