Java vs. Kotlin, Stateful Function Edition

Java vs. Kotlin, Stateful Function Edition

State encapsulation or lamba expression with closure when implementing stateful functions? Is Java or Kotlin better suited for either one of those 2 approaches? Let’s find out…

Use case

To compare implementations in Kotlin and Java, we will build a very simple feature, create a function that can count how many times it was called. Basically we need something that will accept a higher-order function and hold a counter to be incremented each time that higher-order function is invoked.

Stateful Function with State Encapsulation in Kotlin

Here we can take advantage of the ability for a Kotlin class to implement a native function type ((T) -> R in this case), and Kotlin’s operator overloading feature to override the invoke() operator, which allows to treat a class instance as a function:

We just store the higher-order function f (which turns out to be our { i: Int -> i * i } function) and delegate to it when we invoke our top level class InvokeAndCount through the invoke operator.

Stateful Function with State Encapsulation in Java

Let’s see now one possible way to implement the same functionalities in Java:

By having our class directly implementing the java.util.function.Function interface, we effectively mimic the Kotlin’s way to implement a native functional type. This is not demonstrated in the above example, but our instance of InvokeandCount class can also be passed as a parameter to any other methods accepting a lambda expression i.e. a Function type.

Lambda Expression with Closure in Kotlin

The concept here is to take a function as a parameter f (which turns out to be our { i: Int -> i * i } function) and return a new function that will call the first function f(i) and increment our counter:

The code is very concise with functional types declarations e.g. (Int) -> Int. Also, in Kotlin we can access and modify any variable declared outside the scope of a lambda expression, which makes it trivial to increment our count variable.

Lambda Expression with Closure in Java

The equivalent Java implementation would look like this below. Interestingly, even if the type definitions are more verbose, it’s also self describing, it’s pretty clear just by looking at the types that we declare a function that takes a function as its parameter (UnaryOperator) and returns another function (UnaryOperator) :

One thing different though from the Kotlin example just above is that, in Java, any variable declared outside the scope of a lambda expression has to be effective final to be referenced by the lambda expression, which means it can’t be modified. This is why here we have to use a trick to use a reference to the holder of our counter (an array in this example) instead of the counter variable itself, as the reference to the array itself never change. This is an undesirable hack for sure, so ideally we never should do that… which might be a trigger to rethink our design…

Conclusion

Those examples highlight some differences between Java and Kotlin, for briefty purpose, thread safety and generic mechanism to store/retrieve any kind of state were skipped, but either using Java or Kotlin, in most cases I would stick with state encapsulation instead of lambda expression + closure, for 2 reasons:

  1. Easier to read and reason about the code
  2. Despite everything functional programming bring to the table (pure functions with no side effects, immutability, etc.), OOP still has its place in some specific scenarios… in the case you have to keep some internal states in your apis/components/etc., unless that state is also declared, initialized and only visible from within your own component, leaking your mutable state outside of the code you control is an invitation to the user of your code to manipulate your internal state without your consent… nothing good can come out of it.

Bottom line, the mouse trap is really in the choice of design and not in the choice of language, safe vs. unsafe code is independent of the language used!

%d bloggers like this: