By Walter Nyland
Lambdas, introduced in Java 8, have provoked such a storm of discussion, translated into new books, articles, and further research, that their more compact cousins appear to have been left standing somewhat forlornly along the sidelines. This article will discuss these new compact expression styles introduced in Java 8 together with lambdas, collectively known as "member references", which are either "method references" or "constructor references." Migrating from lambdas to member references should be exactly your piece of cake if you like your code even sparser and even more devoid of ceremony than the code that lambdas leave you with in cutting back the boilerplate from your Java code.
When you compare it to human speech, member references are somewhat like dropping the majority of the letters of words and, instead, referring to them by the one or two letters that you know your hearer understands to refer to complete words known to both parties. At first, this mode of speaking is extremely dense and almost like a dialect in itself. However, once familiarized with it, devoid of verbosity, conversation can be extremely quick and to the point.
Ignoring tools where they exist to simplify learning is foolish. Taking NetBeans IDE as a guide to learning about member references is a logical path to take because the same team of developers implementing member references in Java have provided the related code analyzers in NetBeans IDE.
We begin by looking at method references, after which we deal with constructor references.
To begin learning about method references, let's take as a point of departure the Greeting class, shown below. It has two different kinds of methods, a static factory method createGreeting and an overridden instance method toString. Via method references in Java 8, we can learn how to refer to these two methods in new ways to initialize variables with them, to pass them around, and to execute them.
Code segment 1
Member references, like lambdas, operate specifically in the context of functional interfaces, the concept of which also is new in Java 8. The familiar Runnable class and the ActionListener have, retrospectively, been functional interfaces all along because they each provide a single abstract method. Java 8 comes with new functional operations, two of which we will use to illustrate the relevance of member references in this article:
- java.util.function.Function<P,R>: The Function interface represents a method that receives a parameter of type "P" and returns a result of type "R".
- java.util.function.Supplier<R>: The Supplier interface represents a method that receives no parameters and returns a result of type "R".
Below you see an implementation of the Function interface making use of the Greeting class shown above. When a parameter of type String is received, the Function returns a result of type Greeting by referring to its method createGreeting. In NetBeans IDE, notice the yellow underline and the yellow lightbulb in the left sidebar, informing you that the IDE is aware of a better way to express the Function implementation. It is not wrong to use anonymous inner classes in Java 8 and therefore, as you can see below, no red error mark is shown and no compiler error will occur. However, NetBeans IDE, via the yellow lightbulb, is able to offer to help you make the optional conversion to either lambda expressions or, in this case, because reference is made to the method createGreeting, to method references.
Code segment 2
When you press Alt-Enter or click the lightbulb, the IDE shows that the anonymous inner class can either be expressed as a lambda expression or as a member reference.
Code segment 3
By invoking the proposal to let the IDE convert the anonymous class to a lambda expression, the following code is automatically created for you.
Code segment 4
Lambda expressions are discussed in our previous article Programming with Lambdas in NetBeans 8, so we will not discuss them again here, because we're only using them here to contrast them with member references. However, note that an alternative, slightly more verbose but possibly more readable, style for stating the lambda expression is offered, too, as shown below.
Code segment 5
Here is the result of writing the lambda expression as a block, where a return statement declares the right side of the lambda expression more clearly, but less succinctly than in the default lambda expression.
Code segment 6
Having shown the lambda expressions that can be used in the context of our Greeting class, let's now contrast them with method references. As you can see below, the IDE can convert the anonymous inner class to a member reference.
Code segment 7
Take careful note of the symbol that is used to express a member reference. Instead of the "->" symbol, a "::" is used instead, as shown below.
Code segment 8
While still referencing the createGreeting method, as done by the lambda expressions earlier, the code is considerably more compact, though, until you are used to the style, considerably more dense. To unpack it, look at it carefully and notice it is in the form Object::Method. The object here, Greeting, contains the method createGreeting, which we want to reference. Also notice that, to make the code even more compact, the parentheses of the createGreeting method are omitted.
Not only can the static factory method createGreeting be referenced via a method reference, but the instance method toString, or any other instance method, can be expressed via method references too, as you can see below.
Code segment 9
Above, notice that we are referring to a method that is held by an instance of Greeting. At the point that we refer to the toString method, the Greeting instance does not yet exist. Later, in the apply method, the Greeting instance is created. An alternative style, where you want to refer to methods of existing instances is expressed below, where you see that the Greeting is declared and an instance is initialized, after which reference is made to one of its methods. Instead of the Function interface, here we use the Supplier interface (described above), because we do not want to convert types; instead, we only want to return the result of a method invocation on a Greeting instance.
Code segment 10
Note that, where relevant, super::toString and this::toString are also acceptable constructs.
Switching our focus to constructor references, we rewrite the Greeting class. Firstly, we remove the static factory method; secondly, we add a default constructor. Now we have two constructors, enabling us to learn how to use constructor references in Java 8 to refer to them and to execute them.
Code segment 11
Below, following similar patterns to those introduced in the section on method references above, are the three new ways introduced in Java 8 for referencing constructors, each of which we will now look at more closely.
Code segment 12
Above, three different constructor reference examples are shown, each discussed below, in turn.
- The format of a constructor reference is Object:: new, where Object is always the name of the class that defines the constructor. The method name is always the standard Java new keyword. Here, we initialize a variable of type Supplier<Greeting> with the Greeting class default constructor. The compiler knows that we mean the default constructor and not the parameterized constructor because greetingFactory1, which is the context of the constructor reference, is of type Supplier<Greeting>. The Supplier<Greeting> type does not allow parameters and therefore the compiler assumes the reference is the default constructor.
- In the second example, the context is greetingFactory2, which is of type Function<String,Greeting>, which allows parameters of, in this case, type String. Therefore, this constructor reference enables us to obtain the reference of the parameterized constructor that accepts a String parameter.
- The final example is concerned with an array constructor reference. As can be seen here, we can create an Array an Greeting objects. The Function<int,Greeting> variable type allows a parameter of type Integer, and a Greeting return type. Notice that square brackets are used in the variable type definition, as well as in the constructor reference itself.
In this article, we have discussed member references, a new construct introduced in Java 8 together with lambda expressions. Moreover, we have discussed the topic in the context of the code analyzers introduced in NetBeans 8, which help to quickly identify and convert anonymous inner classes to member references. Of these, the most powerful is a batch tool that lets you search through all your code and identify all anonymous inner classes that can be converted to lambda expressions or member references:
Code segment 13
By using the above tool, the IDE analyzes a scope, such as all projects, or the current project, or a package, or file, and suggests code that can be rewritten to use lambda expressions or member references. Not only is this useful for learning about member references, it makes migrating your code to the new compact coding styles made possible in Java 8 a breeze.