Finally Functional Programming in Java

Written by anicolaspp | Published 2018/02/26
Tech Story Tags: functional-programming | java | scala | higher-order-function | programming

TLDRvia the TL;DR App

In many post we have explored Functional Programming concept on different languages being F# and Scala the focus of the conversation. However, because I have been doing some Java on my workplace, exploring these same concept seems interesting and eyes opening because it has been a long time since last time I seriously used Java.

Higher Order Functions

As explained here Higher order functions, what are they? higher order functions are simple functions that can receive functions as arguments and can return another function as results.

In modern Java, we can easily do this. The syntax is not the best, and because there is not type inference we have to explicitly declare the function type which in Java means some kind of interface. Let’s see how.

First, let’s suppose we have a collections of objects, that is a collection of dogs and we have a function that acts on each dog. We want to be able to invoke this function on each object (dog).

Let’s see how we could create such a function.

@FunctionalInterfaceinterface DogAge { Integer apply_(Dog dog)_;}

List_<Integer>_ getAges_(List<Dog>_ dogs, DogAge f_) {_ List_<Integer>_ ages = new ArrayList_<>()_;

for _(_Dog dog : dogs_) {_        ages.add_(_f.apply_(_dog_))_;  
_}_        return ages;  

}

We define an interface that given a dog, it extracts some Integer value from it. Then, we define a function getAges that apply the passed function (interface for now) to each dog.

Now, we have to create the actual function we want to apply over each dog.

DogAge f = dog -> dog.getAge_()_;

getAges(dogs, f);

Notice, we don’t have to actually define the DogAge implementation as we just to do in older Java. That will be the following way, but please, don’t use it any longer.

DogAge dontUseMe = new DogAge_() {_ @Overridepublic Integer apply_(Dog dog) {_ return dog.getAge_()_;}};

The former is actually generated by the compiler when it sees the first one.

We can go one step deeper and do the following.

getAges(dogs, dog -> dog.getAge());

In here we are passing the function right into the getAges method.

Somehow, getAges is a higher order function since it can receive functions as arguments. Java keeps the signature weird by receiving an interface, but I guess this will be improved in future versions of the language.

In order to have a comparison point, let’s define getAges in Scala and look at the differences. Also, we are going to change the name of functions at once so it is more generic.

def extractStringFromDogs(dogs: List[Dog], f: Dog => String) =dogs.map(f)

in Java, we could do.

@FunctionalInterfaceinterface DogMapper { String apply_(Dog dog)_;}

List<String> extractStringFromDogs(List<Dog> dogs, DogMapper f) {

return dogs.stream().map(dog -> f.apply(dog)).collect(Collectors.toList);  

}

It happens that there is a structure already in Java that solves this same problem. That is Function<A, B>. In order words, we could do.

List<String> extractStringFromDogs(List<Dog> dogs, Function<Dog, String> f) {

return dogs.stream().map(dog -> f.apply(dog)).collect(Collectors.toList);  

}

extractStringFromDogs(dogs, dog -> dog.getName());

Now, what about defining functions that actually return other functions?

In Scala, we could do the following.

scala> def sum(): (Int, Int) => Int = (a, b) => a + bsum: ()(Int, Int) => Int

scala> sum()res1: (Int, Int) => Int = $$Lambda$1067/2036949810@715f45c6

scala> sum()(4,5)res2: Int = 9

scala> res1(2, 3)res3: Int = 5

In here, sum returns a function that can be stored and evaluated at another time. This is very powerful and important construct of functional languages. Can we do the same in Java?

Let’s start by defining our own function type (Functional Interface) for this particular problem.

@FunctionalInterfaceinterface TakeTwo { Integer apply_(Integer a, Integer b)_;}

As we could see, TakeTwo is semantically the same as what we defined in Scala.

Now, we can define the sum method again.

TakeTwo sum_() {_ return (a, b) -> a + b;}

TakeTwo mySum = sum();

Integer finalSum = mySum.apply_(5, 6)_;

This is exactly the same we did in Scala, just that in Scala, the syntax is concise and there is not need to define a Functional Interface to be used as a function type. Yes, the same result is achieved.

Again, we don’t actually have to define TakeTwo ourselves since there is an equivalent interface already define in Java called BiFunction. By using it we could have written sum in the following way.

BiFunction_<Integer, Integer, Integer>_ sum_() {_ return (a, b) -> a + b;}

More Functional Interfaces.

In order to support the effort of functional programming, Java incorporates a lot of these Functional Interfaces. Some of them are:

Consumer

Java:

public interface Consumer_<T> {_ void accept_(T t)_;....}

Scala

T => Unit

Predicate

Java

public interface Predicate_<T> {_ boolean test_(T t)_;...}

Scala

T => boolean

Supplier

Java

public interface Supplier_<T> {_ T get_()_;}

Scala

:=> T

Function

Java

public interface Function_<T, R> {_ R apply_(T t)_;...}

Scala

T => R

BiFunction

Java

public interface BiFunction_<T, U, R> {_ R apply_(T t, U u)_;...}

Scala

(T, U) => R

These are only few of the type of functions (Functional Interfaces) that can be found of the new Java and their counterpart in Scala. Notice that in Scala we don’t have to define any interfaces for them, we just have the functions right there and we can define them as we want.

Conclusions

Somehow, Java is definitely moving towards Functional Programming and even though the syntax is not the most convenient one, the results are the same.

Scala syntax, on the other hand, is far more precise and shows better the intend without the need of creating interfaces as function types.

I just hope Java continues to evolve while reducing verbosity and adding new functional constructs because at the end, we, the engineers, are the one getting real benefits from them.


Published by HackerNoon on 2018/02/26