Minborg

Minborg
Minborg

Thursday, May 7, 2015

A Java 8 functional view on POJOs (Plain Ordinary Java Object)

Background

Since Java was first created back in the 90's, developers have used Plain Ordinary Java Objects (or short "POJOs"). Now that Java 8 is released, we can have another view on the legacy POJOs and work with them in a completely new way. Read this post and try to master the new opportunities with POJOs and Java 8.

Example of a POJO

Consider the following simple POJO that models a City that can have a name and an associated short code:
public class City {

    private String name;
    private String code;

    public City() {
    }

    public City(String name, String code) {
        this.name = name;
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

}
The idea with POJOs is to compose a more precise Object that consists of one or several other objects so that we can build up more and more complex representations of different models we might need. For the moment, we disregard that the POJO should also have additional equals()hashCode() and  toString() methods.

With this class we can easily create cities like this:
// Use a constructor with parameters to create a City
City sf = new City("San Francisco", "SF");
     
// Use a default constructor with no parameters to create a City
City la = new City();
// Set the members using setters
la.setName("Los Angeles");
la.setCode("LA");
This is all old school, but what is really a getter and a setter in a Java 8 functional context? How can you model different constructors in Java 8?

A Functional View of Getters

A getter is something that can take a POJO and convert it into something else. This corresponds to a Java 8 Function. In our City example, the getName() getter corresponds to a Java 8 Function<City, String> because the getter takes a City and turns it into a String. In other words it maps a City to a String. We can easily prove our postulate by the following code snippet:

// Use the City's method references and assign them to functions
Function<City, String> getNameFunction = City::getName;
Function<City, String> getCodeFunction = City::getCode;

System.out.println("The code for "
        + getNameFunction.apply(sf)
        + " is "
        + getCodeFunction.apply(sf));

-> The code for San Francisco is SF
So, now you can use a Function instead of calling the getter directly. This can be very useful when you want to use different getters programatically in your code, for example by providing the getter as a Function in methods you call. It is also handy because it allows you to use the methods programatically without resorting to reflection in some cases.

A Functional View of Setters

The functional view on setters is a slightly more complex than for getters. Here we really use two parameters: the POJO itself and some value we want to set in the POJO. As opposed to a getter, we do not want to return anything. The way to go in Java 8 is to use something called a BiConsumer. As its prefix implies, a BiConsumer takes  two parameter and it "consumes" them both, meaning that they are "swallowed" so that nothing is returned. Again, we can test this thesis very easily like this:
// Use the City's method references and assign them to biconsumers
BiConsumer<City, String> setNameBiConsumer = City::setName;
BiConsumer<City, String> setCodeBiConsumer = City::setCode;
     
City ny = new City();
setNameBiConsumer.accept(ny, "New York");
setCodeBiConsumer.accept(ny, "NY");
So, now you can also use your setters programatically in your Java 8 code.

A Functional View of Constructors

The way of constructing objects in Java is sometimes considered as "magic" but it is really nothing special about a constructor from a functional point of view. A constructor is something like a Function that takes anything from zero to a large number of parameters and produces an Object.

The Default Constructor

In our example we had two constructors and now we will start with looking at the default constructor City(). The constructor apparently creates a City without using any parameter, so in a Java 8 context it is really a Supplier and more specifically, since it supplies cities, it is a Supplier<City>.  Again, we can prove this statement by trying this simple code:
// Use the City's constructor method reference to create
// a default constructor reference.
Supplier<City> defaultConstructor = City::new;

City sd = defaultConstructor.get();
sd.setName("San Diego");
sd.setCode("SD");
This is great! Now we can create different objects using simple constructor references. Very useful in dynamic programming code where you do not know the type of object you want to create until you run your code. Note how we obtain a method reference for the constructor using the ::new reference.

Parameter Constructors

Now it becomes a bit more tricky. A constructor that takes parameters is something more than just a Supplier because it needs the input parameters before the object can be created. In our example we have a constructor with two parameters and thus we can use Java 8's BiFunction. The BiFunction takes two parameters and can return something else, potentially depending on the two parameters. Because our City constructor takes two strings we want to use a BiFunction<String, String, City> which implies that we take two strings and maps them to a City. Again, we put together some code to show the concept:
// Use the City's constructor method reference to create
// a two-parameter constructor reference.
BiFunction<String, String, City> twoParameterConstructor = City::new;

City dc = twoParameterConstructor.apply("Washington, D.C.", "DC");
But wait there! We used the same method reference City::new for both the parameter-less default constructor and the two-parameter constructor, and yet Java 8 can distinguish between them! How can City::new have two different meanings? The answer is that Java 8 is able to infer which constructor is shall select, because it is able to match the different constructors against the expected result. So, because we want a BiFunction, it understands that it shall return a method reference to the two-parameter constructor and not the default constructor with no parameters!

Conclusions

In this post, we have devised a way of obtaining a fully functional view of POJOs. By mastering these techniques, you will be able to shake new life into your old POJOs and your Java 8 code.

Good luck with your functions!







No comments:

Post a Comment