Constructor references in Java (& method references too)
JDK 8 saw the advent of a special feature: constructor references and method references. In this article, Adrian D. Finlay explores how developers can unlock the true potential of constructor references.
Some background on method references
If you didn’t already know, Java Constructors are, themselves, special methods. As such, it will benefit the reader to see a basic example of a method reference, and through understanding this, understand what Constructor References are.
“Method references provide easy-to-read lambda expressions for methods that already have a name.”
“They provide an easy way to refer to a method without executing it.” ( Java, The Complete Reference 9th Ed, by Herbert Schildt)
Method references can refer to both static and instance methods and can be generic. Method References are instances of a functional interface. While Lambda Expressions allow you to create method implementations on the fly, often times one ends up calling another method inside the lambda expression to fulfill what we want done. A more direct way to do this is to use a method reference. This is useful in circumstances where you already have a method that fulfills the implementation of the Functional Interface.
Let’s look at an example with static & instance methods.
The steps for making use of method references are essentially:
- Define a Functional Interface
- Define a method that meets the requirements of the Functional Interface’s abstract method
- Instantiate an instance of the Functional Interface with a method reference to the method defined in step 2. (x :: y )
- Use Functional Interface instance: Instance.AbstractMethod();
This gives a way to create plug-able instances of methods. Lambda Expression and Method References bring a Functional Aspect to Java Programming.
Constructor method references
Let’s get down to the meat and potatoes.
Constructors are methods just like any other. Right? Wrong. They’re a bit special — they’re object initialization methods. Nevertheless, they are a still a method, and there is nothing stopping us from making Constructor Method References like any other method reference.
The first thing that should be obvious to the user is that this basic example is not that useful. It is a rather roundabout way to create an instance of an object. Practically speaking, you almost certainly wouldn’t go through all this trouble to create an instance of an Automobile, but for conceptual completeness, it is included here.
The steps for making use of constructor method references are essentially:
- Define a Functional Interface with an abstract method whose return type is the same as the Object with which you intend to make a Constructor Reference
- Create a class with a constructor that matches the Functional Interface’s abstract method
- Instantiate an instance of the Functional Interface with a method reference to the constructor defined in step #2. (x :: y )
- Instantiate an instance of the class in step#2 using the constructor reference such that MyClass x = ConstructorReference.AbstractMethod (x, y, z…)
Where Constructor References become useful is when they are used in tandem with Generics. By using a generic factory method one can create various types of objects.
Let’s have a peek.
Now, there is a lot to digest here. In fact, the code may appear rather obscure at first glance, if you have never dived into Generics before. Let’s break it down.
The first thing we do is create a generic functional interface. Pay attention to the details. We have four generic type parameters — Ob, X,Y,Z.
Ob — The Class whose constructor we want to reference
X,Y,Z — The arguments to the constructor of said class.
If we substitute the generic method placeholders, the Abstract method could look like this: SomeClass func (String make, String model, int year). Notice that because we made the interface generic, we can specify any return type or type of class we desire. This allows to unlock the true potential of constructor references.
The next two portions are relatively straightforward — We essentially create what is the same class, one generic, and one non-generic to demonstrate their interoperability with the factory method we will later define in the public class. Notice that the Constructors of these classes are compatible with the method signature of FuncInt.func().
Enter into the public class of the file. This method is where the magic happens.
We label the method as static, so we can do without an instance of ConstRefGen and, after all, it’s a factory method. Notice that the factory method has the same generic type parameters as the functional interface. Notice that the return type of the method is Ob which will be whichever class we decide. X,Y,Z , are, of course, the method arguments of a method in Ob. Notice that the function takes an instance of the FuncInt as an argument (with the Class Type and the method arguments as type paramaters) as well as the arguments of the method of the class of type Ob.
Inside the body of the method it calls the method reference and feeds it the arguments passed in factory().
Our first task — create a method reference that complies with FuncInt<>
Here we refer to the constructors of Automobile and Plane respectively.
Our next task — Create an object with a method reference.
To do this, we call factory() and we feed it the Constructor Reference it needs as well as the arguments for the constructor in question as defined by factory ().
factory() can agnostically create constructor references to various methods because it is generic. Because the Plane & Automobile constructors match the method signature of FuncInt.func() they will work with as a method reference with FuncInt.func(). factory() returns an instance of the class in question by calling obj.func(x,y,z) which is a constructor method reference that when evaluated will give you an instance of the class that was specified as an argument to it.
Wrestle with this one for a while — It’s a VERY useful addition to Java ;)
This article was originally published on The Java Report.