bene : studio is a global consultancy, helping startups, enterprises and HealthTech companies to have better product
Functional programming in Java with examples
At bene : studio we love knowledge sharing to help the community of professionals. With 10+ years and over 100 projects behind us, we have a vast amount of experience. This is why we have launched a knowledge base on our blog with regular updates of tutorials, best practices, and open source solutions.
These materials come from our internal workshops with our team of developers and engineers.
Pardon the interruption, we have an important message!
We are looking to expand our team with talented developers. Check out our open positions and apply.
What is functional programming?
” Functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that map values to other values, rather than a sequence of imperative statements which update the running state of the program.”
Wikipedia
To put it simply, functional programming is the process of building software by composing pure functions(a function or a block of code that always returns the same result if the same arguments are passed).
Imperative vs. declarative
Declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow while
Imperative programming is a programming paradigm that uses statements that change a program’s state.
In simpler terms, Imperative programming basically refers to how you do something while the declarative programming approach is what to do.
Let’s take an example to find a simple item (Pencil) in stationery.
For the imperative style of programming, we would do something like this
This approach is a low level where we are telling the code how to do what we want, we initialize a variable found, we loop through a list of stationery items and if we find the pencil, we set found to true and break out of the loop. Then we finally print the result. This approach is pretty noisy. There should be a better way.
Now let’s look at the better way, the declarative approach.
The contains() method is a declarative style of programming that does all the work under the hood without us knowing exactly what is going on. We just tell the code what we want it to do and don’t worry about how it’s done. This makes the code simple and takes out the problem of creating unneeded variables.
Functional programming in Java 8
Java is an imperative programming language, and before Java 7, It was difficult to implement functional programming techniques in Java. This changed with the introduction of multiple functional programming API’s in Java 8. Java 8 came with new additions like lambdas, streams, and functional interfaces which made for the possible implementation of functional programming techniques in Java and the implementation of the declarative style of programming in Java.
We will now see some of these techniques in the below examples.
Brace up! Let’s go.
Practical coding in functional programming showing imperative and declarative approaches
In this section, we’re going to build a simple student grading system and carry out the following operations
- Grouping students in each course by grade
- Filtering students by grade
- Finding students with the highest and lowest grades
- Fetching Mean, Median, and Mode of student grades
First thing first, let’s create our student class
Then let’s instantiate a couple of students and add them to a studentList.
Grouping students in each course by grade
We will carry out the grouping of students by their grades using the imperative approach. To achieve this we will first create a hashmap to house the groups, then loop through the original students’ list and try to group them based on their grades.
In the code above, a lot is currently going on as there are multiple concerns,
How to iterate, what to group, and how to group. This entangled logic will result in poor reuse. By going this route, we are also prone to errors.
Now let’s use the declarative style approach:
We use the streams API to get a stream from the student list and use the collect terminal operation to group the students by grade into a map. As we can see, this approach is cleaner, requires less code and we don’t worry about how the code is doing its job.
PS: For more on streams you can visit this Java Streams
Filtering students by grade
We will also want to filter out students that passed the course with a pass mark of 45
Using the imperative approach, we have code like this. Again as before, we would have to worry about iteration and filtering.
A better approach would be to use the streams filter method which takes in a lambda and returns a new stream.
Predicates
A predicate is a functional interface with a method test that takes in an argument and returns a boolean.
The streams filter method expects a predicate that would be used to evaluate all items in the stream.
Using this approach, we’ve decoupled Iteration from filtering and can further extend this code easily by passing in different filters depending on our needs without changing the logic. Notice in the code below how we filter to get students who scored 90 points and above.
Finding the highest and lowest performing students
Highest Student using the imperative approach
As usual to achieve this the burden of iterating and doing comparisons to determine the highest scoring student falls on us as we have to write how to loop through the student list and how to compare their grades to get the best. This is okay but not functional. Let’s see.
Highest Student using the declarative approach
In the declarative approach above, we pass a comparator with a method reference Student::getOverallGrade to the max method to get the highest grade.
Lowest Student using the imperative approach
Lowest Student using the declarative approach
Finding the mean, median, and mode of student grades
Finding the mean
Using an imperative style approach, we normally would do something like the below,
In the declarative approach, we use method chaining on intermediate operations for processing until we get the result of our computation using a terminal operation. In the code snippet below, we get the average by mapping the student grades to an integer with mapToInt() and calling the average() method on the resulting stream.
Finding the median
The imperative style will give us something like this.
We would sort the list of students according to their grade and then check to see if the list is divisible by 2 to pick its median. This is obviously us telling the program how to do its work and as always requires more code.
As we can see here also, the imperative approach requires more code to be written and is bogus. We have to modify everything ourselves.
Let’s use a declarative style approach as below,
As you can notice from the code snippet above, the amount of code written is relatively smaller and we obtain the median by simply chaining a few methods together. This makes our code a lot cleaner than in the imperative style and as always we look like badass programmers.
Finding the mode
Imperative
Similar to previous examples, the imperative style applied when finding the mode leads to extensive, error-prone code that is difficult to optimize compared to the declarative alternative. For the imperative approach we will have to group the grades and the number of students who scored that particular grade into a hashmap and after loop through the hashmap to get the grade with the highest number of students thus having..
Declarative
With the use of streams, lambda expressions and method chaining we are able to achieve this same result without knowing exactly how its done.
Some cons of functional programming
- Hard to explain: Functional programming has a lot of things going for it, but being easy to explain is not one of them. Learning how to read programs written in a functional style is a difficult skill; people seem to be much better at conceiving of programs as a series of steps to be followed, like a recipe, rather than as a series of calculations to be carried out.
- Increased garbage generation: More garbage is generated when using functional programming as it is against mutation of data. Therefore more variables will be created to handle specific assignments and when not needed, will be required to be garbage collected.
Conclusion
Functional Programming can be very useful as it helps developers to write short and efficient code with the introduction of streams, lambdas, functional interfaces etc. It takes away the problem of having to worry about what is happening in the low level of the code and just focus on what you want to achieve. It also helps in eliminating opportunities that allow the introduction of errors in code and it’s cool to have this in Java.