In our previous article involving Java; the new Java 8 code style seems confusing and intimidating. We aimed to give a quick overview into some questions, specifically relating to the Java world such as; Are there really any benefits of this new paradigm? In which situations can you leverage the new features?
In this article we hope to further develop this overview, focusing on “Object Oriented Programming vs. Functional Programming”.
Declarative, Imperative, and Functional Programming
Functional programming is a term that means different things to different people. At the heart of functional programming is thinking about your problem domain in terms of immutable values and functions that translate between them. People also correlate functional programming with imperative style.
So, let’s see a side by side comparisons of declarative vs imperative styles first:
The below diagram shows a better illustration of the correlation among them:
Functional Styles in Java 8
In recent years, functional programming has become popular because of its suitability in concurrent, parallel, and event-driven programming. Most modern programming languages support functional programming styles. Since the release of Java 1.0 in 1996, the language has been evolving gradually over the years to accommodate the new technologies and practices of the industry. Some would say that the changes introduced in Java 8 are in some ways more profound than any other change introduced on Java’s history. Java 8 introduces Lambda expression and Streams that reflect a move away from the classical object-oriented paradigm towards the functional style spectrum.
Let see another simple Java code. Suppose you have a list of integers from 1 to 5. You would compute the sum of the squares of all odd integers in the list as follows:
This example uses a for-each loop that performs an external iteration on the list of integers. Simply put, the client code (the for loop in this case) pulls the elements out of collection and applies the logic to get the result.
Consider the following snippet of code that uses a stream and lambda expression to compute the sum of the squares of all odd integers in the same list:
Functional Programming Concepts:
There are a lot of terminologies and concepts in the functional programming world. In this section we want to focus discussion on the main concept.
Purity / Pure Functions
In its simplest term, pure functions only operate on their input parameters with 2 main characteristics:
- The function always returns the same result if the same arguments are passed on. It does not depend on any state, or data, change during a program’s execution. It must only depend on its input arguments.
- The function does not produce any observable side effects such as network requests, input and output devices, or data mutation.
For demonstration purposes here is an example of an impure function that calculates the price of a product including tax:
Above function is obviously able to calculate the price correctly. But, if you look closely, it depends on an external tax variable. How to make it more (pure) functional? Two alternatives as below:
It passes the requirements for a function to be declared pure: It doesn’t depend on any external input, it doesn’t mutate any data and it doesn’t have any side effects. If you run this function with the same input 100,000,000 times it will always produce the same result. One of the major benefits of using pure functions is they are immediately testable. They will always produce the same result if you pass in the same arguments.
Are side effects bad? Of course not, since programs have to interface to the real world, some parts of every program must be impure. However, the goal is to minimize the amount of impure code and segregate it from the rest of our program.
Higher-order Functions and Function Composition
Higher-order functions either take functions as parameters, return functions or both. Higher-order functions can either take other functions as arguments or return them as results. In functional programming, functions are our building blocks. We write them to do very specific tasks and then we put them together like Lego™ blocks. This is called Function Composition.
For example we have some string utility functions as below:
So we can easily use startWithJ and fourLetterLong to build our new function:
Or even modify and combine with other functions:
Common functional patterns in general are Map, Filter, and Reduce.
Benefit of functional styles:
But why should we embrace these changes? Why should we use time and effort getting comfortable with the functional styles when we can solve the problems in pure OOP?
- The functional styles introduced in Java 8, helps us reduce the gap between the business logic and the code. It allows them to tell the story together in a natural flow on a higher level. Instead of saying how you want things done, you can say what you want to be done.
- A lot of boilerplates can be removed, which results in cleaner and more concise code.
- We will be able to create code that will be more robust, focused, and easier to reuse.
- Thanks to lambdas, we can now do lazy evaluations. When sending a lambda expression as a method argument, the java compiler will evaluate it when it's called in the method. This is different from normal method arguments, which are evaluated straight away.
- Allow us to create unit tests that are clean, small, and quick to write. We can stub the code we're testing, using lambdas.
It should be clear though, the fact that Java now has some of the features that are available and largely used in functional languages doesn’t automatically make it a functional language as well. Java is still at its core, Object Oriented and will continue to be so. As with most things in technology, these new features are merely tools that you can add to your toolbox. It’s your job to understand them and knowing when it makes sense to use them. Mastering the new functional styles of Java will be challenging for OOP developers, but it is a challenge that should be very welcome.
Functional programming is not…
- map and reduce
- lambda functions
Functional programming is more a mindset than a particular set of tools or languages. So, embrace functional programming, as well as the good old OOP, and use them together to write even greater code!
- Kishori Sharan. “Java Language Features – With Modules, Streams, Threads, I/O, and Lambda Expressions, 2nd Edition”, Apress, 2018. ISBN 978-1-4842-3348-1.
- Richard Warburton. “Java 8 Lambdas – Functional Programming for the Masses”, O’Reilly, 2014. ISBN 978-1-4493-7077-0.
- An easier path to functional programming in Java: https://www.ibm.com/developerworks/library/j-java8idioms1/index.html
- A Guide to Streams in Java 8: In-Depth Tutorial with Examples: https://stackify.com/streams-guide-java-8/
- An Overview of Functional Style Programming in Java 8: https://jlordiales.me/2014/11/01/overview-java-8/
- Pure Functions vs Impure Functions: http://net-informations.com/js/iq/pure.htm
- Why You Should Embrace Functional Programming in Java 8: http://www.deadcoderising.com/why-you-should-embrace-lambdas-in-java-8/