Functional Programming dates all the way back to Lisp in the 1950s. Imperative, procedural, and object-oriented programming each had their long runs as paradigms, but functional programming never quite broke through to the mainstream.

But with Java 8’s support of lambda expressions, type inference, syntactic sugar for static methods, and new APIs like Stream and CompletableFuture, there is a renaissance around functional programming. Oracle is basically taking 9 million Java developers back to the future.

(Related: Another take on functional programming)

So let’s take a look at some of the key concepts that Java shops need to be familiar with as they weigh the opportunities that functional programming offers, and try to form a strategy for how they approach functional directions in Java 8:

Lambdas: It’s already a very common pattern in Java to do callbacks using anonymous classes when there is an outside event happening. Lambdas are basically just that, but with more syntactic sugar around it. The simplest explanation of lambdas is that they’re a function: They take input, process it and yield output. You can do this in Java now. But it’s extremely cumbersome. You have to define a new class that implements one method that basically does what the lambda does. So it’s like five or six lines of code instead of having it written as a single line of code. But for what it does, it makes no difference. It just looks different.

Functions: Functions are extremely simple. They are nameless interfaces with a single method—they just rely on input and output type. In libraries, you have well-established names for common functions that are always the same and have the same semantics, often even across languages, such as map, filter, reduce, fold, etc. A function takes a string and returns something, which makes it extremely easy to reuse and compose. When defining classes in Java, you as the developer have come up with your own API for each class you define: [setFoo(foo), getBar() and doStuff()].

This means that every new user of this API will have to learn your custom API—the names that you come up with—which makes it very hard to compose things, since you needed to relearn the API for every single new API you use. This makes object-oriented components extremely hard to compose and reuse. Another benefit with using functions is that they only do one well-defined thing, which makes them very easy to reuse, a big contrast to objects that tend to do multiple things and have tangled responsibilities, sometimes dependent on the context in which they are used.

Immutable State: In the single-threaded world of the 1980s and 1990s, dealing with state was easier. But managing state with multicore… well, it opens Pandora’s box. Many Java developers are trying to shoehorn all of these states into a perceived reality of running on a single core. Functional programming languages like Scala, Erlang and Haskell encourage immutable states, which basically means being able to better reason about the effects of what is happening at a given time. In Java the default is mutable, so you have to be very careful when using lambdas in Java if you assume a functional style, because all the collections are by default mutable. So if you use it like you used to, then things will break all over the place.