Connecting...

W1siziisimnvbxbpbgvkx3rozw1lx2fzc2v0cy9zawduawz5lxrly2hub2xvz3kvanbnl2jhbm5lci1kzwzhdwx0lmpwzyjdxq

Finally Functional Programming in Java

W1siziisijiwmtgvmdmvmtivmtevmdyvmzevnzaxlzffs0xpltfvtxrqnkk2wwxzumkxwkxyus5wbmcixsxbinailcj0ahvtyiisijkwmhg5mdbcdtawm2uixv0

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.

@FunctionalInterface
interface 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 (interfacefor 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() {
    @Override
    public 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.

@FunctionalInterface
interface 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 + b
sum: ()(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.

@FunctionalInterface
interface 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 Scalawe 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.