Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
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 ayes 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 be passed as arguments and can be returned from another function.
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() {@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.
@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.
Finally Functional Programming in Java was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.