Notice that I have to explicitly state "BiFunction". There is also "Function" for single-argument functions, and nothing for more arguments (there are also `Runnable`, `Consumer` and `Producer` for fewer arguments in-or-out). This is because BiFunction isn't actually a function - it's an INTERFACE! Any class that implements 'apply' and 'andThen' functions with the right signatures will satisfy it, and can be passed in. You can make your own class and Java will happily accept it into this method.
Java then just adds some nice syntactic sugar to make stuff look like functions. E.g., if you do want to define an anonymous lambda like
(x,y) -> "" + x + y;
What happens under-the-hood is that Java defines an anonymous BiFunction class. You can assign it to a variable and do everything you would want to do with an object:
I can call bif.toString() and all those other default methods defined on objects in Java. It's really not a function, it's an object holding a function:
and if you were to go and implement your own BiFunction as above (filling in the blanks) - you could pass it around exactly the same places as your "anonymous lambda" and it would work exactly the same way because it IS the same thing.
Like I said, a very object-oriented approach to functionality.