Using Functions As Java Operators In Adobe ColdFusion 2025.0.8
While AI is the marquee feature of Adobe ColdFusion 2025 update 8, one of the minor release notes that really caught my eye was the ability to pass ColdFusion functions into Java to be used as an operator / callback where a "Single Abstract Method" (SAM) it accepted. As much as I absolutely love the fact that we (CFML developers) can dip down into the Java layer for additional super powers, there's always been a bit of an impedance mismatch for more complex interactions at said touch-point. Passing ColdFusion functions as operators is going to be a nice removal of friction.
To demonstrate, let's look at Java's Atomic Integer. The "Atomic" package provides a number of lock-free, thread-safe data structures that provide safe concurrency without heavy(ier)-weight locking mechanics. I often use the Atomic Integer as a thread-safe counter. But, it can do more than just increment and decrement.
For example, it has two methods that accept a SAM operator that implements the mutation logic:
updateAndGet( IntUnaryOperator )accumulateAndGet( int, IntBinaryOperator )
Let's try to use both of these method, passing-in a ColdFusion closure, to see how they work. In the following CFML code, we're going to be trying three things:
Call the
.incrementAndGet()method which takes no arguments. This is our common base-case and has been available forever in ColdFusion.Call the
.updateAndGet()method which takes a unary operator and returns the next counter value. Our operator will increment the counter by2on each call.Call the
accumulateAndGet()method which takes a binary operator and returns the next counter value. Our operator will count up to the given maximum (inclusive) and then reset to1.
Note that we're not doing anything interesting with our closures — no "dynamic proxy" nonsense — just passing around raw UDFs (User Defined Functions).
<cfscript>
// ColdFusion language extensions (global functions).
include "/core/cfmlx.cfm";
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
// The "Atomic" package provides light-weight thread-safe data structures that provide
// for concurrency without traditional (heavier-weight) locking mechanics. In this
// case, we'll use the Atomic Integer to create a thread-safe counter.
counter = new java( "java.util.concurrent.atomic.AtomicInteger" )
.init( 0 )
;
// Base case: no arguments, simple increment.
echo( "<p> No operator - straightforward java calls. </p>" );
dump([
counter.incrementAndGet(),
counter.incrementAndGet(),
counter.incrementAndGet(),
]);
// Adobe ColdFusion 2025: we can pass a unary (one argument) operator into Java.
echo( "<p> Unary operator - takes previous value and returns new value. </p>" );
dump([
counter.updateAndGet( ( prev ) => prev + 2 ),
counter.updateAndGet( ( prev ) => prev + 2 ),
counter.updateAndGet( ( prev ) => prev + 2 ),
]);
// Adobe ColdFusion 2025: we can pass a binary (two argument) operator into Java.
echo( "<p> Binary operator - takes previous/next values and returns new value. </p>" );
// I increment up until the upper limit (inclusive) is reached, then reset to 1.
countUpAndThenReset = ( prev, upperLimit ) => {
return ( prev < upperLimit )
? ++prev
: 1
;
};
// Note: 3 is the "upper limit" in this case.
dump([
counter.accumulateAndGet( 3, countUpAndThenReset ),
counter.accumulateAndGet( 3, countUpAndThenReset ),
counter.accumulateAndGet( 3, countUpAndThenReset ),
counter.accumulateAndGet( 3, countUpAndThenReset ),
counter.accumulateAndGet( 3, countUpAndThenReset ),
counter.accumulateAndGet( 3, countUpAndThenReset ),
counter.accumulateAndGet( 3, countUpAndThenReset ),
]);
</cfscript>
It all looks like native, seamless CFML syntax! And when we run this Adobe ColdFusion 2025.0.8 code, we get the following output:
The ColdFusion user defined functions that we passed into the Java layer were successfully consumed as both unary and binary operators. This is super exciting!
Epilogue On Lexically-Bound Closures
Having nothing to do with ColdFusion specifically, the Atomic callbacks in this demo are intended to be "side-effect free". Meaning, they are pure functions that act solely based on the invocation arguments. That said, since I'm using closures as my operators, they retain the lexically-scoped context. Which means, the operators can technically reach out-of-scope and consume non-local values.
I tried this to see if it works, and it does (not shown in the demo). The operators, executed inside of the Java context, can reach back into the lexically-bound ColdFusion context and consume functions and data. So, that's pretty cool!
Want to use code from this post? Check out the license.
Reader Comments
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →