I have been playing with BiFunction
(java.util.function
). I ran some examples and and I have one question.
Is there a limita
I don't know of an inherent expression-level limit to this, but it will in practice be contrained by the JVM method size limit of 65534 bytes:
The fact that end_pc is exclusive is a historical mistake in the design of the Java Virtual Machine: if the Java Virtual Machine code for a method is exactly 65535 bytes long and ends with an instruction that is 1 byte long, then that instruction cannot be protected by an exception handler. A compiler writer can work around this bug by limiting the maximum size of the generated Java Virtual Machine code for any method, instance initialization method, or static initializer (the size of any code array) to 65534 bytes.
Source: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3
First of all, that’s not specific to BiFunction
in any way. So you are basically asking, how deep you can nest method invocations and the simple answer is that the Java programming language does not specify a limitation per se.
There are technical limitations which may restrict the number but these are, well, technical limitations, not limitation by specification. They might be lifted when the technology evolves without changes in the specification.
As Alain O'Dea has explained, a method’s code size is limited to 65535 bytes or to 65534 bytes if the last instruction ought to be covered by an exception handler. The amount of nested method calls supported by this code size depends on some factors. E.g., you are using an interface
and interface method invocation use more bytes than concrete class method invocations (invokevirtual instructions), further, you are using BiFunction<Integer, Integer, Integer>
rather than the straight-forward IntBinaryOperator
so every invocation involves boxing of the int
values which requires additional code.
But there is another technical limitation anyway, the compiler implementation. When trying to compile your example with a higher nesting count, javac
ran from the command line terminated with a stackoverflow at 1500 nested invocation, while Netbeans (using the same compiler code as javac
) managed to compile 2000 nested invocations before the IDE started to exhibit strange behavior (I guess, it doesn’t handle stack overflows of the compiler/ syntax highlighter very well).
That suggests that the IDE had a higher stack size or other differences in the environmental setup affected the initial stack depth before the expression got parsed. This leads to the conclusion that there is no hard limit in practice. You may be able to write code which one compiler manages to compile without problems whereas another one bails out— not a good idea to max this out.
After all, the equivalent of your question’s code could be written as:
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;
}
though I think, what you have in mind, is more like:
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;
}
or
public static OptionalInt methodContainingMethod(
IntBinaryOperator theFunction, int... arguments) {
return IntStream.of(arguments).reduce(theFunction);
}