We’ve added some more goodies to our BoxLang Java interop, this time around method references and high order functions. These are things CF has never let you do, leaving Java interop a second-class citizen in the language.
CF already allows you to grab a reference to a UDF or closure as a variable, pass it around, and invoke it.
myInstance = new myClass();
myInstanceMethod = myInstance.myMethod;
myInstanceMethod();
BL also allows you to grab a reference to a static method from a Box class as well:
myStaticUDF = src.test.java.TestCases.phase3.StaticTest::sayHello;
myStaticUDF();
Now, in BoxLang, we’ve elevated Java methods, both instance and static to also be objects you can pass around, invoke, and send into a higher order function (a function that accepts functions).
When you reference a method on a Java class without the parenthesis (just like our BL examples above), you will get a special Function instance that wraps up the Java method, allowing it to be treated as a function, passed into any argument which is typed as a function, and invoked headlessly.
Here we capture the static valueOf()
Method from the Java String class, and place it into a variable, where we invoke it.
import java:java.lang.String;
javaStaticMethod = java.lang.String::valueOf;
result = javaStaticMethod( "test" ) // New string of "test"
This example captures the toUpperCase
method from a String instance. Note, the method is still bound to the original String instance, and when invoked, will be invoked against that original instance
javaInstanceMethod = "my string".toUpperCase
result = javaInstanceMethod() // "MY STRING"
And finally, here we use a Java method to pass directly in place of a UDF or Closure to a higher order function.
import java.util.Collections;
// Use the compare method from the Java reverse order comparator to sort a BL array
[ 1, 7, 3, 99, 0 ].sort( Collections.reverseOrder().compare ) // [ 99, 7, 3, 1, 0 ]
We grab the compare
method from Java’s reverse order comparator and pass it directly into the array sort method in BoxLang, reversing our array!
Sadly, you can’t use many java methods with CF/BL’s reduce, each, map higher order BIFs because they pass additional arguments to the function and java is strict on arguments so examples like this do not work
import java:java.lang.Math;
[ 1, 2.4, 3.9, 4.5 ].map( Math::floor ) // Error because floor only accepts a single arg!
since Math.floor()
accepts a single argument but arrayMap()
passes 3 arguments to the map function.
This new functionality mixed with our new ability to pass BoxLang closures into java methods that accept functional interfaces/lambdas gives great two-way compatibility to pass methods and functions in both directions between Java and BoxLang!
In this example, we get a Java Stream from our BoxLang array and then call the Java filter()
method on the stream, passing a BoxLang Lambda in, which is automatically converted to the correct Java functional interface!
fruits = [ "apple", "banana", "cherry", "ananas", "elderberry" ];
result = fruits.stream()
.filter( fruit -> fruit.startsWith( "a" ) )
.toList();