Académique Documents
Professionnel Documents
Culture Documents
ByCayS.Horstmann,March25,2014
7 Comments
ThesinglemostimportantchangeinJava8enablesfaster,clearercodingandopensthedoor
tofunctionalprogramming.Here'showitworks.
Java was designed in the 1990s as an objec t-oriented programming language, when objec t-oriented
programming was the princ ipal paradigm for software development. Long before there was objec toriented programming, there were func tional programming languages suc h as Lisp and Sc heme, but
their benefits were not muc h apprec iated outside ac ademic c irc les. Rec ently, func tional programming
has risen in importanc e bec ause it is well suited for c onc urrent and event-driven (or "reac tive")
programming. That doesn't mean that objec t orientation is bad. Instead, the winning strategy is to
blend objec t-oriented and func tional programming. This makes sense even if you are not interested in
c onc urrenc y. For example, c ollec tion libraries c an be given powerful APIs if the language has a
c onvenient syntax for func tional expressions.
The princ ipal enhanc ement in Java 8 is the addition of func tional programming c onstruc ts to its
objec t-oriented roots. In this artic le, I demonstrate the basic syntax and examine how to use it
several important c ontexts. The key points are:
A lambda expression is a bloc k of c ode with parameters.
Use a lambda expression whenever you want a bloc k of c ode exec uted at a later point in time.
Lambda expressions c an be c onverted to func tional interfac es.
Lambda expressions c an ac c ess effec tively final variables from the enc losing sc ope.
Method and c onstruc tor referenc es refer to methods or c onstruc tors without invoking them.
You c an now add default and static methods to interfac es that provide c onc rete
implementations.
You must resolve any c onflic ts between default methods from multiple interfac es.
Why Lambdas?
A lambda expression is a bloc k of c ode that you c an pass around so it c an be exec uted later, just
onc e or multiple times. Before getting into the syntax (or even the c urious name), let's step bac k and
see where you have used similar c ode bloc ks in Java all along.
When you want to do work in a separate thread, you put the work into the runmethod of
a Runnable, like this:
1
2
3
4
5
6
7
classWorkerimplementsRunnable{
publicvoidrun(){
for(inti=0;i<1000;i++)
doWork();
}
...
}
Then, when you want to exec ute this c ode, you c onstruc t an instanc e of the Workerc lass. You c an
then submit the instanc e to a thread pool, or keep it simple and start a new thread:
1
2
Workerw=newWorker();
newThread(w).start();
The key point is that the runmethod c ontains c ode that you want to exec ute in a separate thread.
Consider sorting with a c ustom c omparator. If you want to sort strings by length instead of the
default dic tionary order, you c an pass a Comparatorobjec t to the sortmethod:
1
2
3
classLengthComparatorimplementsComparator<String>{
publicintcompare(Stringfirst,Stringsecond){
returnInteger.compare(first.length(),second.length());
4
5
6
7
}
}
Arrays.sort(strings,newLengthComparator());
The sortmethod keeps c alling the comparemethod, rearranging the elements if they are out of
order, until the array is sorted. You give the sortmethod a snippet of c ode needed to c ompare
elements, and that c ode is integrated into the rest of the sorting logic , whic h you'd probably not
c are to reimplement. Note that the c all Integer.compare(x, y)returns zero if xand yare equal, a
negative number if x<y, and a positive number if x>y. This static method was added to Java 7.
You shouldn't c ompute x - yto c ompare xand ybec ause that c omputation c an overflow for large
operands of opposite sign.
As another example for deferred exec ution, c onsider a button c allbac k. You put the c allbac k ac tion
into a method of a c lass implementing the listener interfac e, c onstruc t an instanc e, and register the
instanc e with the button. That happens so often that many programmers use the "anonymous
instanc e of anonymous c lass" syntax:
1
2
3
4
5
button.setOnAction(newEventHandler<ActionEvent>(){
publicvoidhandle(ActionEventevent){
System.out.println("Thanksforclicking!");
}
});
What matters is the c ode inside the handlemethod. That c ode is exec uted whenever the button is
c lic ked.
Sinc e Java 8 positions JavaFX as the suc c essor to the Swing GUI toolkit, I use JavaFX in these
examples. (See Eric Bruno's posts for more information on JavaFX. Ed.) The details don't matter. In
every user interfac e toolkit, be it Swing, JavaFX, or Android, you give a button some c ode that you
want to run when the button is c lic ked.
In all three examples, you saw the same approac h. A bloc k of c ode was passed to someone a
thread pool, a sortmethod, or a button. The c ode was c alled at some later time.
Up to now, giving someone a bloc k of c ode hasn't been easy in Java. You c ouldn't just pass c ode
bloc ks around. Java is an objec t-oriented language, so you had to c onstruc t an objec t belonging to a
c lass that has a method with the desired c ode.
In other languages, it is possible to work with bloc ks of c ode direc tly. The Java designers have
resisted adding this feature for a long time. After all, a great strength of Java is its simplic ity and
c onsistenc y. A language c an bec ome an unmaintainable mess if it inc ludes every feature that yields
marginally more-c onc ise c ode. However, in those other languages, it isn't just easier to spawn a
thread or to register a button-c lic k handler; large swaths of their APIs are simpler, more c onsistent,
and more powerful. In Java, one c ould have written similar APIs that take objec ts of c lasses
implementing a partic ular func tion, but suc h APIs would be unpleasant to use.
For some time now, the question was not whether to augment Java for func tional programming, but
how to do it. It took several years of experimentation before a design emerged that is a good fit for
Java. In the next sec tion, you will see how you c an work with bloc ks of c ode in Java 8.
Integer.compare(first.length(),second.length())
What are first and sec ond? They are both strings! Java is a strongly typed language, and we must
spec ify that as well:
1
(Stringfirst,Stringsecond)
You have just seen your first lambda expression! Suc h an expression is simply a bloc k of c ode,
together with the spec ific ation of any variables that must be passed to the c ode.
Why the name? Many years ago, before there were any c omputers, the logic ian Alonzo Churc h
wanted to formalize what it means for a mathematic al func tion to be effec tively c omputable.
(Curiously, there are func tions that are known to exist, but nobody knows how to c ompute their
values.) He used the Greek letter lambda () to mark parameters. Had he known about the Java API,
he would have written:
1
first.second.Integer.compare(first.length(),second.length())
Why the letter ? Did Churc h run out of other letters of the alphabet? Ac tually, the
venerablePrinc ipia Mathematic a used the ac c ent to denote free variables, whic h inspired Churc h to
use an upperc ase lambda () for parameters. But in the end, he switc hed to the lowerc ase version.
Ever sinc e, an expression with parameter variables has been c alled a "lambda expression."
You have just seen one form of lambda expressions in Java: parameters, the -> arrow, and an
expression. If the c ode c arries out a c omputation that doesn't fit in a single expression, write it
exac tly like you would have written a method: enc losed in {}and with explic it returnstatements.
For example,
1
2
3
4
5
(Stringfirst,Stringsecond)>{
if(first.length()<second.length())return1;
elseif(first.length()>second.length())return1;
elsereturn0;
}
If a lambda expression has no parameters, you still supply empty parentheses, just as with a
parameterless method:
1
()>{for(inti=0;i<1000;i++)doWork();}
If the parameter types of a lambda expression c an be inferred, you c an omit them. For example,
1
2
3
Comparator<String>comp
=(first,second)
>Integer.compare(first.length(),second.length());
Here, the c ompiler c an deduc e that firstand secondmust be strings bec ause the lambda
expression is assigned to a string c omparator. (We will have a c loser look at this assignment later.)
If a method has a single parameter with inferred type, you c an even omit the parentheses:
1
2
3
EventHandler<ActionEvent>listener=event>
System.out.println("Thanksforclicking!");
// Instead of (event) -> or (ActionEvent event) ->
You c an add annotations or the finalmodifier to lambda parameters in the same way as for method
parameters:
1
2
(finalStringname)>...
(@NonNullStringname)>...
You never spec ify the resulttype of a lambda expression. It is always inferred from c ontext. For
example, the expression
1
(Stringfirst,Stringsecond)>Integer.compare(first.length(),second.length())
Functional Interfaces
As we disc ussed, there are many existing interfac es in Java that enc apsulate bloc ks of c ode, suc h
as Runnableor Comparator. Lambdas are bac kwards c ompatible with these interfac es.
You c an supply a lambda expression whenever an objec t of an interfac e with a single abstrac t
method is expec ted. Suc h an interfac e is c alled a func tional interfac e.
You may wonder why a func tional interfac e must have a single abstrac t method. Aren't all methods
in an interfac e abstrac t? Ac tually, it has always been possible for an interfac e to redec lare methods
from the Objectc lass suc h as toStringor clone, and these dec larations do not make the methods
abstrac t. (Some interfac es in the Java API redec lare Objectmethods in order to attac h javadoc
c omments. Chec k out the Comparator API for an example.) More importantly, as you will see shortly,
in Java 8, interfac es c an dec lare non-abstrac t methods.
To demonstrate the c onversion to a func tional interfac e, c onsider the Arrays.sortmethod. Its
sec ond parameter requires an instanc e of Comparator, an interfac e with a single method. Simply
supply a lambda:
1
2
Arrays.sort(words,
(first,second)>Integer.compare(first.length(),second.length()));
Behind the sc enes, the Arrays.sortmethod rec eives an objec t of some c lass that
implementsComparator<String>. Invoking the comparemethod on that objec t exec utes the body of
the lambda expression. The management of these objec ts and c lasses is c ompletely implementation
dependent, and it c an be muc h more effic ient than using traditional inner c lasses. It is best to think
of a lambda expression as a func tion, not an objec t, and to ac c ept that it c an be passed to a
func tional interfac e.
This c onversion to interfac es is what makes lambda expressions so c ompelling. The syntax is short
and simple. Here is another example:
1
2
button.setOnAction(event>
System.out.println("Thanksforclicking!"));
BiFunction<String,String,Integer>comp
=(first,second)>Integer.compare(first.length(),second.length());
However, that does not help you with sorting. There is no Arrays.sortmethod that wants
aBiFunction. If you have used a func tional programming language before, you may find this c urious.
But for Java programmers, it's pretty natural. An interfac e suc h as Comparatorhas a spec ific
purpose, not just a method with given parameter and return types. Java 8 retains this flavor. When
you want to do something with lambda expressions, you still want to keep the purpose of the
expression in mind, and have a spec ific func tional interfac e for it.
The interfac es in java.util.functionare used in several Java 8 APIs, and you will likely see them
elsewhere in the future. But keep in mind that you c an equally well c onvert a lambda expression into
a func tional interfac e that is a part of whatever API you use today. Also, you c an tag any func tional
interfac e with the @FunctionalInterfaceannotation. This has two advantages. The c ompiler
c hec ks that the annotated entity is an interfac e with a single abstrac t method. And the javadoc
page inc ludes a statement that your interfac e is a func tional interfac e. You are not required to use
the annotation. Any interfac e with a single abstrac t method is, by definition, a func tional interfac e.
But using the @FunctionalInterfaceannotation is a good idea.
Finally, note that c hec ked exc eptions matter when a lambda is c onverted to an instanc e of a
func tional interfac e. If the body of a lambda expression c an throw a c hec ked exc eption, that
exc eption needs to be dec lared in the abstrac t method of the target interfac e. For example, the
following would be an error:
1
2
Runnablesleeper=()>{System.out.println("Zzz");Thread.sleep(1000);};
// Error: Thread.sleep can throw a checkedInterruptedException
Bec ause the Runnable.runc annot throw any exc eption, this assignment is illegal. To fix the error,
you have two c hoic es. You c an c atc h the exc eption in the body of the lambda expression. Or you
c an assign the lambda to an interfac e whose single abstrac t method c an throw the exc eption. For
example, the callmethod of the Callableinterfac e c an throw any exc eption. Therefore, you c an
assign the lambda to aCallable<Void>(if you add a statement return null).
Method References
Sometimes, there is already a method that c arries out exac tly the ac tion that you'd like to pass on
to some other c ode. For example, suppose you simply want to print the eventobjec t whenever a
button is c lic ked. Of c ourse, you c ould c all
1
button.setOnAction(event>System.out.println(event));
It would be nic er if you c ould just pass the printlnmethod to the setOnActionmethod. Here is
how you do that:
1
button.setOnAction(System.out::println);
Arrays.sort(strings,String::compareToIgnoreCase)
As you c an see from these examples, the ::operator separates the method name from the name of
an objec t or c lass. There are three princ ipal c ases:
object::instanceMethod
Class::staticMethod
Class::instanceMethod
In the first two c ases, the method referenc e is equivalent to a lambda expression that supplies the
parameters of the method. As already mentioned, System.out::printlnis equivalent to x ->
System.out.println(x). Similarly, Math::powis equivalent to (x, y) -> Math.pow(x, y). In the
third c ase, the first parameter bec omes the target of the method. For
example,String::compareToIgnoreCaseis the same as(x, y) -> x.compareToIgnoreCase(y).
When there are multiple overloaded methods with the same name, the c ompiler will try to find from
the c ontext whic h one you mean. For example, there are two versions of the Math.maxmethod, one
for integers and one for double values. Whic h one gets pic ked depends on the method parameters of
the func tional interfac e to whic h Math::maxis c onverted. Just like lambda expressions, method
referenc es don't live in isolation. They are always turned into instanc es of func tional interfac es.
You c an c apture the thisparameter in a method referenc e. For example, this::equalsis the same
as x -> this.equals(x). It is also valid to use super. The method
expressionsuper::instanceMethoduses this as the target and invokes the superc lass version of the
given method. Here is an artific ial example that shows the mec hanic s:
1
2
3
4
5
6
7
8
9
classGreeter{
publicvoidgreet(){
System.out.println("Hello,world!");
}
}
classConcurrentGreeterextendsGreeter{
publicvoidgreet(){
Threadt=newThread(super::greet);
10
11
12
t.start();
}
}
When the thread starts, its Runnableis invoked, and super::greetis exec uted, c alling
thegreetmethod of the superc lass. (Note that in an inner c lass, you c an c apture the this referenc e
of an enc losing c lass as EnclosingClass.this::methodorEnclosingClass.super::method.)
Constructor References
Construc tor referenc es are just like method referenc es, exc ept that the name of the method isnew.
For example, Button::newis a referenc e to a Buttonc onstruc tor. Whic h c onstruc tor? It depends on
the c ontext. Suppose you have a list of strings. Then, you c an turn it into an array of buttons, by
c alling the c onstruc tor on eac h of the strings, with the following invoc ation:
1
2
3
List<String>labels=...;
Stream<Button>stream=labels.stream().map(Button::new);
List<Button>buttons=stream.collect(Collectors.toList());
Details of the stream, map, and collectmethods are beyond the sc ope of this artic le. For now,
what's important is that the mapmethod c alls the Button(String)c onstruc tor for
eac hlistelement. There are multiple Buttonc onstruc tors, but the c ompiler pic ks the one with
aStringparameter bec ause it infers from the c ontext that the c onstruc tor is c alled with a string.
You c an form c onstruc tor referenc es with array types. For example, int[]::newis a c onstruc tor
referenc e with one parameter: the length of the array. It is equivalent to the lambda expressionx > new int[x].
Array c onstruc tor referenc es are useful to overc ome a limitation of Java. It is not possible to
c onstruc t an array of a generic type T. The expression new T[n]is an error sinc e it would be erased
to new Object[n]. That is a problem for library authors. For example, suppose we want to have an
array of buttons. The Streaminterfac e has a toArraymethod that returns anObjectarray:
1
Object[]buttons=stream.toArray();
But that is unsatisfac tory. The user wants an array of buttons, not objec ts. The stream library
solves that problem with c onstruc tor referenc es. Pass Button[]::newto the toArraymethod:
1
Button[]buttons=stream.toArray(Button[]::new);
The toArraymethod invokes this c onstruc tor to obtain an array of the c orrec t type. Then it fills and
returns the array.
Variable Scope
Often, you want to be able to ac c ess variables from an enc losing method or c lass in a lambda
expression. Consider this example:
1
2
3
4
5
6
7
8
9
publicstaticvoidrepeatMessage(Stringtext,intcount){
Runnabler=()>{
for(inti=0;i<count;i++){
System.out.println(text);
Thread.yield();
}
};
newThread(r).start();
}
Consider a c all:
1
repeatMessage("Hello",1000);
Now look at the variables countand textinside the lambda expression. Note that these variables
are not defined in the lambda expression. Instead, these are parameter variables of
the repeatMessagemethod.
If you think about it, something not obvious is going on here. The c ode of the lambda expression may
run long after the c all to repeatMessagehas returned and the parameter variables are gone. How do
the textand countvariables stay around?
To understand what is happening, we need to refine our understanding of a lambda expression. A
lambda expression has three ingredients:
1. A bloc k of c ode
2. Parameters
3. Values for the free variables; that is, the variables that are not parameters and not defined
inside the c ode
In our example, the lambda expression has two free variables, textand count. The data struc ture
representing the lambda expression must store the values for these variables, in our
c ase, "Hello"and 1000. We say that these values have been c aptured by the lambda expression.
(It's an implementation detail how that is done. For example, one c an translate a lambda expression
into an objec t with a single method, so that the values of the free variables are c opied into instanc e
variables of that objec t.)
The tec hnic al term for a bloc k of c ode together with the values of the free variables is ac losure. If
someone gloats that their language has c losures, rest assured that Java has them as well. In Java,
lambda expressions are c losures. In fac t, inner c lasses have been c losures all along. Java 8 gives us
c losures with an attrac tive syntax.
As you have seen, a lambda expression c an c apture the value of a variable in the enc losing sc ope. In
Java, to ensure that the c aptured value is well defined, there is an important restric tion. In a lambda
expression, you c an only referenc e variables whose value doesn't c hange. For example, the following
is illegal:
1
2
3
4
5
6
7
8
9
10
publicstaticvoidrepeatMessage(Stringtext,intcount){
Runnabler=()>{
while(count>0){
// Error: Can't m utate captured variable
count;
System.out.println(text);
Thread.yield();
}
};
newThread(r).start();
}
There is a reason for this restric tion. Mutating variables in a lambda expression is not thread-safe.
Consider a sequenc e of c onc urrent tasks, eac h updating a shared c ounter.
1
2
3
4
intmatches=0;
for(Pathp:files)
newThread(()>{if(phassomeproperty)matches++;}).start();
// Illegal to m utate m atches
If this c ode were legal, it would be very, very bad. The inc rement matches++is not atomic , and
there is no way of knowing what would happen if multiple threads exec ute that inc rement
c onc urrently.
Inner c lasses c an also c apture values from an enc losing sc ope. Before Java 8, inner c lasses were
allowed to ac c ess only final loc al variables. This rule has now been relaxed to matc h that for lambda
expressions. An inner c lass c an ac c ess any effec tively final loc al variable; that is, any variable whose
value does not c hange.
Don't c ount on the c ompiler to c atc h all c onc urrent ac c ess errors. The prohibition against mutation
holds only for loc al variables. If matchesis an instanc e or static variable of an enc losing c lass, then
no error is reported, even though the result is just as undefined.
Also, it's perfec tly legal to mutate a shared objec t, even though it is unsound. For example,
1
2
3
4
List<Path>matches=newArrayList<>();
for(Pathp:files)
newThread(()>{if(phassomeproperty)matches.add(p);}).start();
// Legal to m utate m atches, but unsafe
Note that the variable matchesis effec tively final. (An effec tively final variable is a variable that is
never assigned a new value after it has been initialized.) In our c ase, matchesalways refers to the
same ArrayListobjec t. However, the objec t is mutated, and that is not thread-safe. If multiple
threads c all add, the result is unpredic table.
There are safe mec hanisms for c ounting and c ollec ting values c onc urrently. You may want to use
streams to c ollec t values with c ertain properties. In other situations, you may want to use threadsafe c ounters and c ollec tions.
As with inner c lasses, there is an esc ape hatc h that lets a lambda expression update a c ounter in an
enc losing loc al sc ope. Use an array of length 1, like this:
1
2
int[]counter=newint[1];
button.setOnAction(event>counter[0]++);
Of c ourse, c ode like this is not thread-safe. For a button c allbac k, that doesn't matter, but in
general, you should think twic e before using this tric k.
The body of a lambda expression has the same sc ope as a nested bloc k. The same rules for name
c onflic ts and shadowing apply. It is illegal to dec lare a parameter or a loc al variable in the lambda
that has the same name as a loc al variable.
1
2
3
4
Pathfirst=Paths.get("/usr/bin");
Comparator<String>comp=
(first,second)>Integer.compare(first.length(),second.length());
// Error: Variable first already defined
Inside a method, you c an't have two loc al variables with the same name. Therefore, you c an't
introduc e suc h variables in a lambda expression either. When you use the thiskeyword in a lambda
expression, you refer to the thisparameter of the method that c reates the lambda. For example,
c onsider
1
2
3
publicclassApplication(){
publicvoiddoWork(){
Runnablerunner=()>{...;System.out.println(this.toString());...};
4
5
6
...
}
}
Default Methods
Many programming languages integrate func tion expressions with their c ollec tions library. This often
leads to c ode that is shorter and easier to understand than the loop equivalent. For example,
c onsider a loop:
1
2
for(inti=0;i<list.size();i++)
System.out.println(list.get(i));
There is a better way. The library designers c an supply a forEachmethod that applies a func tion to
eac h element. Then you c an simply c all
1
list.forEach(System.out::println);
That's fine if the c ollec tions library has been designed from the ground up. But the Java c ollec tions
library was designed many years ago, and there is a problem. If the Collectioninterfac e gets new
methods, suc h as forEach, then every program that defines its own c lass
implementing Collectionwill break until it, too, implements that method. That is simply
unac c eptable in Java.
The Java designers dec ided to solve this problem onc e and for all by allowing interfac e methods with
c onc rete implementations (c alled default methods). Those methods c an be safely added to existing
interfac es. In this sec tion, we'll look at default methods in detail. In Java 8, the forEachmethod has
been added to the Iterableinterfac e, a superinterfac e of Collection, using the mec hanism that I
will desc ribe here.
Consider this interfac e:
1
2
3
4
interfacePerson{
longgetId();
defaultStringgetName(){return"JohnQ.Public";}
}
The interfac e has two methods: getId, whic h is an abstrac t method, and the default
methodgetName. A c onc rete c lass that implements the Personinterfac e must, of c ourse, provide an
implementation of getId, but it c an c hoose to keep the implementation of getNameor to override it.
Default methods put an end to the c lassic pattern of providing an interfac e and an abstrac t c lass
that implements most or all of its methods, suc h
as Collection/AbstractCollectionorWindowListener/WindowAdapter. Now, you c an just
implement the methods in the interfac e.
What happens if the exac t same method is defined as a default method in one interfac e and then
again as a method of a superc lass or another interfac e? Languages suc h as Sc ala and C++ have
c omplex rules for resolving suc h ambiguities. Fortunately, the rules in Java are muc h simpler. They
are:
1. Superc lasses win. If a superc lass provides a c onc rete method, default methods with the same
name and parameter types are simply ignored.
2. Interfac es c lash. If a super interfac e provides a default method, and another interfac e supplies
a method with the same name and parameter types (default or not), then you must resolve the
interfaceNamed{
defaultStringgetName(){returngetClass().getName()+"_"+hashCode();}
}
classStudentimplementsPerson,Named{
...
}
The c lass inherits two inc onsistent getNamemethods provided by the Personand Namedinterfac es.
Rather than c hoosing one over the other, the Java c ompiler reports an error and leaves it up to the
programmer to resolve the ambiguity. Simply provide a getNamemethod in the Studentc lass. In that
method, you c an c hoose one of the two c onflic ting methods, like this:
1
2
3
4
classStudentimplementsPerson,Named{
publicStringgetName(){returnPerson.super.getName();}
...
}
Now assume that the Namedinterfac e does not provide a default implementation for getName:
1
2
3
interfaceNamed{
StringgetName();
}
Can the Studentc lass inherit the default method from the Personinterfac e? This might be
reasonable, but the Java designers dec ided in favor of uniformity. It doesn't matter how two
interfac es c onflic t. If at least one interfac e provides an implementation, the c ompiler reports an
error, and the programmer must resolve the ambiguity.
If neither interfac e provides a default for a shared method, then we are in the pre-Java 8 situation
and there is no c onflic t. An implementing c lass has two c hoic es: implement the method, or leave it
unimplemented. In the latter c ase, the c lass is itself abstrac t.
I just disc ussed name c lashes between two interfac es. Now c onsider a c lass that extends a
superc lass and implements an interfac e, inheriting the same method from both. For example, suppose
that Personis a c lass and Studentis defined as:
1
classStudentextendsPersonimplementsNamed{...}
In that c ase, only the superc lass method matters, and any default method from the interfac e is
simply ignored. In our example, Studentinherits the getNamemethod from Person, and it doesn't
make any differenc e whether the Namedinterfac e provides a default for getNameor not. This is the
"c lass wins" rule. The "c lass wins" rule ensures c ompatibility with Java 7. If you add default methods
to an interfac e, it has no effec t on c ode that worked before there were default methods. But be
warned: You c an never make a default method that redefines one of the methods in
the Objectc lass. For example, you c an't define a default method for toStringor equals, even
though that might be attrac tive for interfac es suc h as List. As a c onsequenc e of the "c lasses win"
rule, suc h a method c ould never win against Object.toStringor Object.equals.
publicinterfacePath{
publicstaticPathget(Stringfirst,String...more){
returnFileSystems.getDefault().getPath(first,more);
}
...
}
publicstaticvoidshuffle(List<?>list)
publicdefaultvoidshuffle()
publicstatic<T>List<T>nCopies(intn,To)
// Constructs a list of n instances of o
c ould be a static method of the Listinterfac e. Then you would c all List.nCopies(10,
"Fred")instead of Collections.nCopies(10, "Fred")and it would be c lear to the reader that the
result is a List.
It is unlikely that the Java c ollec tions library will be refac tored in this way, but when you implement
your own interfac es, there is no longer a reason to provide a separate c ompanion c lass for utility
methods.
In Java 8, static methods have been added to quite a few interfac es. For example,
theComparatorinterfac e has a very useful static c omparing method that ac c epts a "key extrac tion"
func tion and yields a c omparator that c ompares the extrac ted keys. To c omparePersonobjec ts by
name, use Comparator.comparing(Person::name).
Conclusion
In this artic le, I c ompared strings by length with the lambda expression (first, second) ->
Integer.compare(first.length(), second.length()). But with the static comparemethod, we
c an do muc h better and simply use Comparator.compare(String::length). This is a fitting way of
c losing this artic le bec ause it demonstrates the power of working with func tions.
The comparemethod turns a func tion (the key extrac tor) into a more c omplex func tion (the keybased c omparator). Suc h "higher-order func tions" are disc ussed in more detail in my book, as well as
in various online resourc es for Java 8.
Cay S. Horstmann is is a professor of c omputer sc ienc e at San Jose State University, a Java
Champion, and a frequent speaker at c omputer industry c onferenc es. He is the author ofJava SE 8
For the Really Impatient, published by Addison-Wesley, from whic h this artic le is adapted.