You are on page 1of 27

JavaSE8:LambdaQuickStart

Overview

Purpose
ThistutorialintroducesthenewlambdaexpressionsincludedinJavaPlatform
StandardEdition8(JavaSE8).

TimetoComplete
Approximately1hour

Introduction
LambdaexpressionsareanewandimportantfeatureincludedinJavaSE8.
Theyprovideaclearandconcisewaytorepresentonemethodinterface
usinganexpression.LambdaexpressionsalsoimprovetheCollection
librariesmakingiteasiertoiteratethrough,filter,andextractdatafroma
Collection.Inaddition,newconcurrencyfeaturesimproveperformancein
multicoreenvironments.
ThisOraclebyExample(OBE)providesanintroductiontolambda
expressionsincludedinJavaSE8.Anintroductiontoanonymousinner
functionsisprovided,followedbyadiscussionoffunctionalinterfacesandthe
newlambdasyntax.Then,examplesofcommonusagepatternsbeforeand
afterlambdaexpressionsareshown.
ThenextsectionreviewsacommonsearchusecaseandhowJavacodecan
beimprovedwiththeinclusionoflambdaexpressions.Inaddition,someof
thecommonfunctionalinterfaces,PredicateandFunction,providedin
java.util.functionareshowninaction.
TheOBEfinishesupwithareviewofhowtheJavacollectionhasbeen
updatedwithlambdaexpressions.
Thesourcecodeforallexamplesisprovidedtoyou.

HardwareandSoftwareRequirements
Thefollowingisalistofhardwareandsoftwarerequirements:
JavaDevelopmentKit(JDK8)earlyaccess

JavaDevelopmentKit(JDK8)earlyaccess
NetBeans7.4

Prerequisites
Toruntheexamples,youmusthaveanearlyaccessversionofJDK8anda
copyofNetBeans7.4orlater.LinkscanbefoundatthemainLambdasite.
Foryourconvenience,herearethedirectlinks.
JavaDevelopmentKit8(JDK8)EarlyAccess
Recommended:DownloadtheJDK8APIDocsfromthesame
page.
NetBeans7.4orlater.Version7.4ofNetBeansincludessupportforJDK
8.
Note:Buildsareprovidedforallmajoroperatingsystems.ThisOBEwas
developedusingLinuxMint13(Ubuntu/Debian).
InstallJDK8andNetBeansfollowingtheinstructionsprovidedwiththe
downloads.AddthebindirectoriesforJavaandNetBeanstoyourPATH.
Note:ThisOBEwaslastupdatedDecember2013.

Background

AnonymousInnerClass
InJava,anonymousinnerclassesprovideawaytoimplementclassesthat
mayoccuronlyonceinanapplication.Forexample,inastandardSwingor
JavaFXapplicationanumberofeventhandlersarerequiredforkeyboardand
mouseevents.Ratherthanwritingaseparateeventhandlingclassforeach
event,youcanwritesomethinglikethis.
16
17
18
19
20
21

JButton testButton = new JButton("Test Button");


testButton.addActionListener(new ActionListener(){
@Override public void actionPerformed(ActionEvent ae){
System.out.println("Click Detected by Anon Class");
}
});

Otherwise,aseparateclassthatimplementsActionListenerisrequiredfor
eachevent.Bycreatingtheclassinplace,whereitisneeded,thecodeisa
littleeasiertoread.Thecodeisnotelegant,becausequiteabitofcodeis
requiredjusttodefineonemethod.

FunctionalInterfaces
ThecodethatdefinestheActionListenerinterface,lookssomethinglike
this:

this:
1
2
3
4
5
6
7
8

package java.awt.event;
import java.util.EventListener;
public interface ActionListener extends EventListener {
public void actionPerformed(ActionEvent e);
}

TheActionListenerexampleisaninterfacewithonlyonemethod.With
JavaSE8,aninterfacethatfollowsthispatternisknownasa"functional
interface."
Note:Thistypeofinterface,waspreviouslyknownasaSingleAbstract
Methodtype(SAM).

Usingfunctionalinterfaceswithanonymousinnerclassesareacommon
patterninJava.InadditiontotheEventListenerclasses,interfaceslike
RunnableandComparatorareusedinasimilarmanner.Therefore,
functionalinterfacesareleveragedforusewithlambdaexpressions.

LambdaExpressionSyntax
Lambdaexpressionsaddressthebulkinessofanonymousinnerclassesby
convertingfivelinesofcodeintoasinglestatement.Thissimplehorizontal
solutionsolvesthe"verticalproblem"presentedbyinnerclasses.
Alambdaexpressioniscomposedofthreeparts.
Argument
List

ArrowToken

Body

(intx,int
y)

>

x+y

Thebodycanbeeitherasingleexpressionorastatementblock.Inthe
expressionform,thebodyissimplyevaluatedandreturned.Intheblockform,
thebodyisevaluatedlikeamethodbodyandareturnstatementreturns
controltothecalleroftheanonymousmethod.Thebreakandcontinue
keywordsareillegalatthetoplevel,butarepermittedwithinloops.Ifthebody
producesaresult,everycontrolpathmustreturnsomethingorthrowan
exception.
Takealookattheseexamples:
(int x, int y) -> x + y
() -> 42
(String s) -> { System.out.println(s); }

Thefirstexpressiontakestwointegerarguments,namedxandy,anduses
theexpressionformtoreturnx+y.Thesecondexpressiontakesno
argumentsandusestheexpressionformtoreturnaninteger42.Thethird
expressiontakesastringandusestheblockformtoprintthestringtothe
console,andreturnsnothing.
Withthebasicsofsyntaxcovered,let'slookatsomeexamples.

LambdaExamples

Herearesomecommonusecasesusingthepreviouslycoveredexamples.

RunnableLambda
HereishowyouwriteaRunnableusinglambdas.
6 public class RunnableTest {
7
public static void main(String[] args) {
8
9
System.out.println("=== RunnableTest ===");
10
11
// Anonymous Runnable
12
Runnable r1 = new Runnable(){
13
14
@Override
15
public void run(){
16
System.out.println("Hello world one!");
17
}
18
};
19
20
// Lambda Runnable
21
Runnable r2 = () -> System.out.println("Hello world two!");
22
23
// Run em!
24
r1.run();
25
r2.run();
26
27
}
28 }

Inbothcases,noticethatnoparameterispassedandisreturned.The
Runnablelambdaexpression,whichusestheblockformat,convertsfivelines
ofcodeintoonestatement.

ComparatorLambda
InJava,theComparatorclassisusedforsortingcollections.Inthefollowing
example,anArrayListconsistingofPersonobjectsissortedbasedon

surName.ThefollowingarethefieldsincludedinthePersonclass.
9 public class Person {
10
11
12
13
14
15
16
17

private String givenName;


private String surName;
private int age;
private Gender gender;
private String eMail;
private String phone;
private String address;

ThefollowingcodeappliesaComparatorbyusingananonymousinnerclass
andacouplelambdaexpressions.
10 public class ComparatorTest {
11
12
public static void main(String[] args) {
13
14
List<Person> personList = Person.createShortList();
15
16
// Sort with Inner Class
17
Collections.sort(personList, new Comparator<Person>(){
18
public int compare(Person p1, Person p2){
19
return p1.getSurName().compareTo(p2.getSurName());
20
}
21
});
22
23
System.out.println("=== Sorted Asc SurName ===");
24
for(Person p:personList){
25
p.printName();
26
}
27
28
// Use Lambda instead
29
30
// Print Asc
31
System.out.println("=== Sorted Asc SurName ===");
32
Collections.sort(personList, (Person p1, Person p2) ->
p1.getSurName().compareTo(p2.getSurName()));
33
34
for(Person p:personList){
35
p.printName();
36
}
37
38
// Print Desc
39
System.out.println("=== Sorted Desc SurName ===");
40
Collections.sort(personList, (p1, p2) ->
p2.getSurName().compareTo(p1.getSurName()));
41
42
for(Person p:personList){
43
p.printName();
44
}
45
46
}
47 }

Lines1721areeasilyreplacedbythelambdaexpressiononline32.Notice
thatthefirstlambdaexpressiondeclarestheparametertypepassedtothe
expression.However,asyoucanseefromthesecondexpression,thisis

expression.However,asyoucanseefromthesecondexpression,thisis
optional.Lambdasupports"targettyping"whichinferstheobjecttypefrom
thecontextinwhichitisused.Becauseweareassigningtheresulttoa
Comparatordefinedwithageneric,thecompilercaninferthatthetwo
parametersareofthePersontype.

ListenerLambda
Finally,let'srevisittheActionListenerexample.
13 public class ListenerTest {
14
public static void main(String[] args) {
15
16
JButton testButton = new JButton("Test Button");
17
testButton.addActionListener(new ActionListener(){
18
@Override public void actionPerformed(ActionEvent ae){
19
System.out.println("Click Detected by Anon Class");
20
}
21
});
22
23
testButton.addActionListener(e -> System.out.println("Click
Detected by Lambda Listner"));
24
25
// Swing stuff
26
JFrame frame = new JFrame("Listener Test");
27
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
28
frame.add(testButton, BorderLayout.CENTER);
29
frame.pack();
30
frame.setVisible(true);
31
32
}
33 }

Noticethatthelambdaexpressionispassedasaparameter.Targettypingis
usedinanumberofcontextsincludingthefollowing:
Variabledeclarations
Assignments
Returnstatements
Arrayinitializers
Methodorconstructorarguments
Lambdaexpressionbodies
Conditionalexpressions?:
Castexpressions

Resources
ANetBeansprojectcontainingthesourcefilesfortheexamplescoveredto
thispointisinthefollowingzipfile.
LambdaExamples01.zip

ImprovingCodewithLambdaExpressions

Thissectionbuildsuponthepreviousexamplestoshowyouhowlambda
expressionscanimproveyourcode.Lambdasshouldprovideameansto
bettersupporttheDon'tRepeatYourself(DRY)principleandmakeyourcode
simplerandmorereadable.

ACommonQueryUseCase
Acommonusecaseforprogramsistosearchthroughacollectionofdatato
finditemsthatmatchaspecificcriteria.Intheexcellent"JumpStarting
Lambda"presentationatJavaOne2012,StuartMarksandMikeDuigouwalk
thoughjustsuchausecase.Givenalistofpeople,variouscriteriaareused
tomakerobocalls(automatedphonecalls)tomatchingpersons.Thistutorial
followsthatbasicpremisewithslightvariations.
Inthisexample,ourmessageneedstogetouttothreedifferentgroupsinthe
UnitedStates:
Drivers:Personsovertheageof16
Draftees:Malepersonsbetweentheagesof18and25
Pilots(specificallycommercialpilots):Personsbetweentheagesof23
and65
Theactualrobotthatdoesallthisworkhasnotyetarrivedatourplaceof
business.Insteadofcalling,mailingoremailing,amessageisprintedtothe
console.Themessagecontainsaperson'sname,age,andinformation
specifictothetargetmedium(forexample,emailaddresswhenemailingor
phonenumberwhencalling).

PersonClass
EachpersoninthetestlistisdefinedbyusingthePersonclasswiththe
followingproperties:
10 public class Person {
11
private String givenName;
12
private String surName;
13
private int age;
14
private Gender gender;
15
private String eMail;
16
private String phone;
17
private String address;
18

ThePersonclassusesaBuildertocreatenewobjects.Asamplelistof
peopleiscreatedwiththecreateShortListmethod.Hereisashortcode
fragmentofthatmethod.Note:Allsourcecodeforthistutorialisincludedina
NetBeansprojectthatislinkedattheendofthissection.

128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

public static List<Person> createShortList(){


List<Person> people = new ArrayList<>();
people.add(
new Person.Builder()
.givenName("Bob")
.surName("Baker")
.age(21)
.gender(Gender.MALE)
.email("bob.baker@example.com")
.phoneNumber("201-121-4678")
.address("44 4th St, Smallville, KS 12333")
.build()
);
people.add(
new Person.Builder()
.givenName("Jane")
.surName("Doe")
.age(25)
.gender(Gender.FEMALE)
.email("jane.doe@example.com")
.phoneNumber("202-123-4678")
.address("33 3rd St, Smallville, KS 12333")
.build()
);
people.add(
new Person.Builder()
.givenName("John")
.surName("Doe")
.age(25)
.gender(Gender.MALE)
.email("john.doe@example.com")
.phoneNumber("202-123-4678")
.address("33 3rd St, Smallville, KS 12333")
.build()
);

AFirstAttempt
WithaPersonclassandsearchcriteriadefined,youcanwriteaRoboContact
class.Onepossiblesolutiondefinesamethodforeachusecase:

RoboContactsMethods.java

1
2
3
4
5
6
7
8
9
10
11
12

package com.example.lambda;
import java.util.List;
/**
*
* @author MikeW
*/
public class RoboContactMethods {
public void callDrivers(List<Person> pl){
for(Person p:pl){

12
13
14
15
16

for(Person p:pl){
if (p.getAge() >= 16){
roboCall(p);
}
}

17
}
18
19
public void emailDraftees(List<Person> pl){
20
for(Person p:pl){
21
if (p.getAge() >= 18 && p.getAge() <= 25 && p.getGender()
== Gender.MALE){
22
roboEmail(p);
23
}
24
}
25
}
26
27
public void mailPilots(List<Person> pl){
28
for(Person p:pl){
29
if (p.getAge() >= 23 && p.getAge() <= 65){
30
roboMail(p);
31
}
32
}
33
}
34
35
36
public void roboCall(Person p){
37
System.out.println("Calling " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
38
}
39
40
public void roboEmail(Person p){
41
System.out.println("EMailing " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
42
}
43
44
public void roboMail(Person p){
45
System.out.println("Mailing " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
46
}
47
48 }

Asyoucanseefromthenames(callDrivers,emailDraftees,and
mailPilots)themethodsdescribethekindofbehaviorthatistakingplace.
Thesearchcriteriaisclearlyconveyedandanappropriatecallismadeto
eachroboaction.However,thisdesignhassomenegativesaspects:
TheDRYprincipleisnotfollowed.
Eachmethodrepeatsaloopingmechanism.
Theselectioncriteriamustberewrittenforeachmethod
Alargenumberofmethodsarerequiredtoimplementeachusecase.
Thecodeisinflexible.Ifthesearchcriteriachanged,itwouldrequirea
numberofcodechangesforanupdate.Thus,thecodeisnotvery
maintainable.

RefactortheMethods
Howcantheclassbefixed?Thesearchcriteriaisagoodplacetostart.Iftest
conditionsareisolatedinseparatemethods,thatwouldbeanimprovement.

RoboContactMethods2.java
1 package com.example.lambda;
2
3 import java.util.List;
4
5 /**
6 *
7 * @author MikeW
8 */
9 public class RoboContactMethods2 {
10
11
public void callDrivers(List<Person> pl){
12
for(Person p:pl){
13
if (isDriver(p)){
14
roboCall(p);
15
}
16
}
17
}
18
19
public void emailDraftees(List<Person> pl){
20
for(Person p:pl){
21
if (isDraftee(p)){
22
roboEmail(p);
23
}
24
}
25
}
26
27
public void mailPilots(List<Person> pl){
28
for(Person p:pl){
29
if (isPilot(p)){
30
roboMail(p);
31
}
32
}
33
}
34
35
public boolean isDriver(Person p){
36
return p.getAge() >= 16;
37
}
38
39
public boolean isDraftee(Person p){
40
return p.getAge() >= 18 && p.getAge() <= 25 &&
p.getGender() == Gender.MALE;
41
}
42
43
public boolean isPilot(Person p){
44
return p.getAge() >= 23 && p.getAge() <= 65;
45
}
46
47
public void roboCall(Person p){
48
System.out.println("Calling " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
49
}
50
51
public void roboEmail(Person p){
52
System.out.println("EMailing " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
53
}
54
55
public void roboMail(Person p){
56
System.out.println("Mailing " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
57
}

57
}
58
59 }

Thesearchcriteriaareencapsulatedinamethod,animprovementoverthe
previousexample.Thetestconditionscanbereusedandchangesflowback
throughouttheclass.Howeverthereisstillalotofrepeatedcodeanda
separatemethodisstillrequiredforeachusecase.Isthereabetterwayto
passthesearchcriteriatothemethods?

AnonymousClasses
Beforelambdaexpressions,anonymousinnerclasseswereanoption.For
example,aninterface(MyTest.java)writtenwithonetestmethodthat
returnsaboolean(afunctionalinterface)isapossiblesolution.Thesearch
criteriacouldbepassedwhenthemethodiscalled.Theinterfacelookslike
this:
6 public interface MyTest<T> {
7
public boolean test(T t);
8 }

Theupdatedrobotclasslookslikethis:

RoboContactsAnon.java

9 public class RoboContactAnon {


10
11
public void phoneContacts(List<Person> pl, MyTest<Person>
aTest){
12
for(Person p:pl){
13
if (aTest.test(p)){
14
roboCall(p);
15
}
16
}
17
}
18
19
public void emailContacts(List<Person> pl, MyTest<Person>
aTest){
20
for(Person p:pl){
21
if (aTest.test(p)){
22
roboEmail(p);
23
}
24
}
25
}
26
27
public void mailContacts(List<Person> pl, MyTest<Person> aTest)
{
28
for(Person p:pl){
29
if (aTest.test(p)){
30
roboMail(p);
31
}
32
}
33
}
34

34
35
public void roboCall(Person p){
36
System.out.println("Calling " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
37
}
38
39
public void roboEmail(Person p){
40
System.out.println("EMailing " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
41
}
42
43
public void roboMail(Person p){
44
System.out.println("Mailing " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
45
}
46
47 }

Thatisdefinitelyanotherimprovement,becauseonlythreemethodsare
neededtoperformroboticoperations.However,thereisaslightproblemwith
uglinesswhenthemethodsarecalled.Checkoutthetestclassusedforthis
class:

RoboCallTest03.java

1 package com.example.lambda;
2
3 import java.util.List;
4
5 /**
6 * @author MikeW
7 */
8 public class RoboCallTest03 {
9
10
public static void main(String[] args) {
11
12
List<Person> pl = Person.createShortList();
13
RoboContactAnon robo = new RoboContactAnon();
14
15
System.out.println("\n==== Test 03 ====");
16
System.out.println("\n=== Calling all Drivers ===");
17
robo.phoneContacts(pl,
18
new MyTest<Person>(){
19
@Override
20
public boolean test(Person p){
21
return p.getAge() >=16;
22
}
23
}
24
);
25
26
System.out.println("\n=== Emailing all Draftees ===");
27
robo.emailContacts(pl,
28
new MyTest<Person>(){
29
@Override
30
public boolean test(Person p){
31
return p.getAge() >= 18 && p.getAge() <= 25 &&
p.getGender() == Gender.MALE;
32
}
33
}
34
);

34
35
36
37
38

);
System.out.println("\n=== Mail all Pilots ===");
robo.mailContacts(pl,

39
new MyTest<Person>(){
40
@Override
41
public boolean test(Person p){
42
return p.getAge() >= 23 && p.getAge() <= 65;
43
}
44
}
45
);
46
47
48
}
49 }

Thisisagreatexampleofthe"vertical"probleminpractice.Thiscodeisa
littledifficulttoread.Inaddition,wehavetowritecustomsearchcriteriafor
eachusecase.

LambdaExpressionsGetitJustRight
Lambdaexpressionssolvealltheproblemsencounteredsofar.Butfirsta
littlehousekeeping.

java.util.function
Inthepreviousexample,theMyTestfunctionalinterfacepassedanonymous
classestomethods.However,writingthatinterfacewasnotnecessary.Java
SE8providesthejava.util.functionpackagewithanumberofstandard
functionalinterfaces.Inthiscase,thePredicateinterfacemeetsourneeds.
3 public interface Predicate<T> {
4
public boolean test(T t);
5 }

Thetestmethodtakesagenericclassandreturnsabooleanresult.Thisis
justwhatisneededtomakeselections.Hereisthefinalversionoftherobot
class.

RoboContactsLambda.java

1
2
3
4
5
6
7
8
9
10

package com.example.lambda;
import java.util.List;
import java.util.function.Predicate;
/**
*
* @author MikeW
*/
public class RoboContactLambda {

10 public class RoboContactLambda {


11
public void phoneContacts(List<Person> pl, Predicate<Person>
pred){
12
for(Person p:pl){
13
if (pred.test(p)){
14
roboCall(p);
15
}
16
}
17
}
18
19
public void emailContacts(List<Person> pl, Predicate<Person>
pred){
20
for(Person p:pl){
21
if (pred.test(p)){
22
roboEmail(p);
23
}
24
}
25
}
26
27
public void mailContacts(List<Person> pl, Predicate<Person>
pred){
28
for(Person p:pl){
29
if (pred.test(p)){
30
roboMail(p);
31
}
32
}
33
}
34
35
public void roboCall(Person p){
36
System.out.println("Calling " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
37
}
38
39
public void roboEmail(Person p){
40
System.out.println("EMailing " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
41
}
42
43
public void roboMail(Person p){
44
System.out.println("Mailing " + p.getGivenName() + " " +
p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
45
}
46
47 }

Withthisapproachonlythreemethodsareneeded,oneforeachcontact
method.ThelambdaexpressionpassedtothemethodselectsthePerson
instancesthatmeetthetestconditions.

VerticalProblemSolved
Lambdaexpressionssolvetheverticalproblemandallowtheeasyreuseof
anyexpression.Takealookatthenewtestclassupdatedforlambda
expressions.

RoboCallTest04.java

1 package com.example.lambda;

1 package com.example.lambda;
2
3 import java.util.List;
4 import java.util.function.Predicate;
5
6 /**
7 *
8 * @author MikeW
9 */
10 public class RoboCallTest04 {
11
12
public static void main(String[] args){
13
14
List<Person> pl = Person.createShortList();
15
RoboContactLambda robo = new RoboContactLambda();
16
17
// Predicates
18
Predicate<Person> allDrivers = p -> p.getAge() >= 16;
19
Predicate<Person> allDraftees = p -> p.getAge() >= 18 &&
p.getAge() <= 25 && p.getGender() == Gender.MALE;
20
Predicate<Person> allPilots = p -> p.getAge() >= 23 &&
p.getAge() <= 65;
21
22
System.out.println("\n==== Test 04 ====");
23
System.out.println("\n=== Calling all Drivers ===");
24
robo.phoneContacts(pl, allDrivers);
25
26
System.out.println("\n=== Emailing all Draftees ===");
27
robo.emailContacts(pl, allDraftees);
28
29
System.out.println("\n=== Mail all Pilots ===");
30
robo.mailContacts(pl, allPilots);
31
32
// Mix and match becomes easy
33
System.out.println("\n=== Mail all Draftees ===");
34
robo.mailContacts(pl, allDraftees);
35
36
System.out.println("\n=== Call all Pilots ===");
37
robo.phoneContacts(pl, allPilots);
38
39
}
40 }

NoticethataPredicateissetupforeachgroup:allDrivers,allDraftees,
andallPilots.YoucanpassanyofthesePredicateinterfacestothe
contactmethods.Thecodeiscompactandeasytoread,anditisnot
repetitive.

Resources
TheNetBeansprojectwiththesourcecodeisincludedinthefollowingzipfile.
RoboCallExample.zip

Thejava.util.functionPackage

Thejava.util.functionPackage

Thejava.util.functionPackage
Ofcourse,PredicateisnottheonlyfunctionalinterfaceprovidedwithJava
SE8.Anumberofstandardinterfacesaredesignedasastartersetfor
developers.
Predicate:Apropertyoftheobjectpassedasargument
Consumer:Anactiontobeperformedwiththeobjectpassedas

argument
Function:TransformaTtoaU
Supplier:ProvideaninstanceofaT(suchasafactory)
UnaryOperator:AunaryoperatorfromT>T
BinaryOperator:Abinaryoperatorfrom(T,T)>T

Inaddition,manyoftheseinterfacesalsohaveprimitiveversions.Thisshould
giveyouagreatstartingpointforyourlambdaexpressions.

EasternStyleNamesandMethodReferences
Whenworkingonthepreviousexample,Idecideditwouldbenicetohavea
flexibleprintingsystemforthePersonclass.Onefeaturerequirementisto
displaynamesinbothawesternstyleandaneasternstyle.IntheWest,
namesaredisplayedwiththegivennamefirstandthesurnamesecond.In
manyeasterncultures,namesaredisplayedwiththesurnamefirstandthe
givennamesecond.

AnOldStyleExample
HereisanexampleofhowtoimplementaPersonprintingclasswithout
lambdasupport.

Person.java

128
public void printWesternName(){
129
130
System.out.println("\nName: " + this.getGivenName() + " " +
this.getSurName() + "\n" +
131
"Age: " + this.getAge() + " " + "Gender: " +
this.getGender() + "\n" +
132
"EMail: " + this.getEmail() + "\n" +
133
"Phone: " + this.getPhone() + "\n" +
134
"Address: " + this.getAddress());
135
}
136
137
138
139
public void printEasternName(){
140
141
System.out.println("\nName: " + this.getSurName() + " " +
this.getGivenName() + "\n" +
142
"Age: " + this.getAge() + " " + "Gender: " +
this.getGender() + "\n" +
143
"EMail: " + this.getEmail() + "\n" +
144
"Phone: " + this.getPhone() + "\n" +

144
145
146

"Phone: " + this.getPhone() + "\n" +


"Address: " + this.getAddress());

Amethodexistsforeachstylethatprintsoutaperson.

TheFunctionInterface
TheFunctioninterfaceisusefulforthisproblem.Ithasonlyonemethod
applywiththefollowingsignature:
publicRapply(Tt){}

IttakesagenericclassTandreturnsagenericclassR.Forthisexample,
passthePersonclassandreturnaString.Amoreflexibleprintmethodfor
personcouldbewrittenlikethis:

Person.java

123
124
125
126

public String printCustom(Function <Person, String> f){


return f.apply(this);
}

Thatisquiteabitsimpler.AFunctionispassedtothemethodandastring
returned.Theapplymethodprocessesalambdaexpressionwhich
determineswhatPersoninformationisreturned.
HowaretheFunctionsdefined?Hereisthetestcodethatcallstheprevious
method.

NameTestNew.java

9 public class NameTestNew {


10
11
public static void main(String[] args) {
12
13
System.out.println("\n==== NameTestNew02 ===");
14
15
List<Person> list1 = Person.createShortList();
16
17
// Print Custom First Name and e-mail
18
System.out.println("===Custom List===");
19
for (Person person:list1){
20
System.out.println(
21
person.printCustom(p -> "Name: " + p.getGivenName() +
" EMail: " + p.getEmail())
22
);
23
}
24
25
26
// Define Western and Eastern Lambdas

26
// Define Western and Eastern Lambdas
27
28
Function<Person, String> westernStyle = p -> {
29
return "\nName: " + p.getGivenName() + " " + p.getSurName()
+ "\n" +
30
"Age: " + p.getAge() + " " + "Gender: " +
p.getGender() + "\n" +
31
"EMail: " + p.getEmail() + "\n" +
32
"Phone: " + p.getPhone() + "\n" +
33
"Address: " + p.getAddress();
34
};
35
36
Function<Person, String> easternStyle = p -> "\nName: " +
p.getSurName() + " "
37
+ p.getGivenName() + "\n" + "Age: " + p.getAge() + "
"+
38
"Gender: " + p.getGender() + "\n" +
39
"EMail: " + p.getEmail() + "\n" +
40
"Phone: " + p.getPhone() + "\n" +
41
"Address: " + p.getAddress();
42
43
// Print Western List
44
System.out.println("\n===Western List===");
45
for (Person person:list1){
46
System.out.println(
47
person.printCustom(westernStyle)
48
);
49
}
50
51
// Print Eastern List
52
System.out.println("\n===Eastern List===");
53
for (Person person:list1){
54
System.out.println(
55
person.printCustom(easternStyle)
56
);
57
}
58
59
60
}
61 }

Thefirstloopjustprintsgivennameandtheemailaddress.Butanyvalid
expressioncouldbepassedtotheprintCustommethod.Easternand
westernprintstylesaredefinedwithlambdaexpressionsandstoredina
variable.Thevariablesarethenpassedtothefinaltwoloops.Thelambda
expressionscouldveryeasilybeincorporatedintoaMaptomaketheirreuse
mucheasier.Thelambdaexpressionprovidesagreatdealofflexibility.

SampleOutput
Hereissomesampleoutputfromtheprogram.
==== NameTestNew02 ===
===Custom List===
Name: Bob EMail: bob.baker@example.com
Name: Jane EMail: jane.doe@example.com
Name: John EMail: john.doe@example.com
Name: James EMail: james.johnson@example.com
Name: Joe EMail: joebob.bailey@example.com

Name: Joe EMail: joebob.bailey@example.com


Name: Phil EMail: phil.smith@examp;e.com
Name: Betty EMail: betty.jones@example.com
===Western List===
Name: Bob Baker
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333
Name: Jane Doe
Age: 25 Gender: FEMALE
EMail: jane.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
Name: John Doe
Age: 25 Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
Name: James Johnson
Age: 45 Gender: MALE
EMail: james.johnson@example.com
Phone: 333-456-1233
Address: 201 2nd St, New York, NY 12111
Name: Joe Bailey
Age: 67 Gender: MALE
EMail: joebob.bailey@example.com
Phone: 112-111-1111
Address: 111 1st St, Town, CA 11111
Name: Phil Smith
Age: 55 Gender: MALE
EMail: phil.smith@examp;e.com
Phone: 222-33-1234
Address: 22 2nd St, New Park, CO 222333
Name: Betty Jones
Age: 85 Gender: FEMALE
EMail: betty.jones@example.com
Phone: 211-33-1234
Address: 22 4th St, New Park, CO 222333
===Eastern List===
Name: Baker Bob
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333
Name: Doe Jane
Age: 25 Gender: FEMALE
EMail: jane.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
Name: Doe John
Age: 25 Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

Address: 33 3rd St, Smallville, KS 12333


Name: Johnson James
Age: 45 Gender: MALE
EMail: james.johnson@example.com
Phone: 333-456-1233
Address: 201 2nd St, New York, NY 12111
Name: Bailey Joe
Age: 67 Gender: MALE
EMail: joebob.bailey@example.com
Phone: 112-111-1111
Address: 111 1st St, Town, CA 11111
Name: Smith Phil
Age: 55 Gender: MALE
EMail: phil.smith@examp;e.com
Phone: 222-33-1234
Address: 22 2nd St, New Park, CO 222333
Name: Jones Betty
Age: 85 Gender: FEMALE
EMail: betty.jones@example.com
Phone: 211-33-1234
Address: 22 4th St, New Park, CO 222333

Resources
TheNetBeansprojectwiththesourcecodefortheexamplesinthissectionis
includedinthefollowingzipfile.
LambdaFunctionExamples.zip

LambdaExpressionsandCollections

TheprevioussectionintroducedtheFunctioninterfaceandfinishedup
examplesofbasiclambdaexpressionsyntax.Thissectionreviewshow
lambdaexpressionsimprovetheCollectionclasses.

LambdaExpressionsandCollections
Intheexamplescreatedsofar,thecollectionsclasseswereusedquiteabit.
However,anumberofnewlambdaexpressionfeatureschangetheway
collectionsareused.Thissectionintroducesafewofthesenewfeatures.

ClassAdditions
Thedrivers,pilots,anddrafteessearchcriteriahavebeenencapsulatedinthe
SearchCriteriaclass.
SearchCriteria.java

SearchCriteria.java

1 package com.example.lambda;
2
3 import java.util.HashMap;
4 import java.util.Map;
5 import java.util.function.Predicate;
6
7 /**
8 *
9 * @author MikeW
10 */
11 public class SearchCriteria {
12
13
private final Map<String, Predicate<Person>> searchMap = new
HashMap<>();
14
15
private SearchCriteria() {
16
super();
17
initSearchMap();
18
}
19
20
private void initSearchMap() {
21
Predicate<Person> allDrivers = p -> p.getAge() >= 16;
22
Predicate<Person> allDraftees = p -> p.getAge() >= 18 &&
p.getAge() <= 25 && p.getGender() == Gender.MALE;
23
Predicate<Person> allPilots = p -> p.getAge() >= 23 &&
p.getAge() <= 65;
24
25
searchMap.put("allDrivers", allDrivers);
26
searchMap.put("allDraftees", allDraftees);
27
searchMap.put("allPilots", allPilots);
28
29
}
30
31
public Predicate<Person> getCriteria(String PredicateName) {
32
Predicate<Person> target;
33
34
target = searchMap.get(PredicateName);
35
36
if (target == null) {
37
38
System.out.println("Search Criteria not found... ");
39
System.exit(1);
40
41
}
42
43
return target;
44
45
}
46
47
public static SearchCriteria getInstance() {
48
return new SearchCriteria();
49
}
50 }

ThePredicatebasedsearchcriteriaarestoredinthisclassandavailablefor
ourtestmethods.

Looping

ThefirstfeaturetolookatisthenewforEachmethodavailabletoany
collectionclass.HereareacoupleofexamplesthatprintoutaPersonlist.
Test01ForEach.java

11 public class Test01ForEach {


12
13
public static void main(String[] args) {
14
15
List<Person> pl = Person.createShortList();
16
17
System.out.println("\n=== Western Phone List ===");
18
pl.forEach( p -> p.printWesternName() );
19
20
System.out.println("\n=== Eastern Phone List ===");
21
pl.forEach(Person::printEasternName);
22
23
System.out.println("\n=== Custom Phone List ===");
24
pl.forEach(p -> { System.out.println(p.printCustom(r ->
"Name: " + r.getGivenName() + " EMail: " + r.getEmail())); });
25
26
}
27
28 }

Thefirstexampleshowsastandardlambdaexpressionwhichcallsthe
printWesternNamemethodtoprintouteachpersoninthelist.Thesecond
exampledemonstratesamethodreference.Inacasewhereamethod
alreadyexiststoperformanoperationontheclass,thissyntaxcanbeused
insteadofthenormallambdaexpressionsyntax.Finally,thelastexample
showshowtheprintCustommethodcanalsobeusedinthissituation.
Noticetheslightvariationinvariablenameswhenonelambdaexpressionis
includedinanother.
Youcaniteratethroughanycollectionthisway.Thebasicstructureissimilar
totheenhancedforloop.However,includinganiterationmechanismwithin
theclassprovidesanumberofbenefits.

ChainingandFilters
Inadditiontoloopingthroughthecontentsofacollection,youcanchain
methodstogether.Thefirstmethodtolookatisfilterwhichtakesa
Predicateinterfaceasaparameter.
ThefollowingexampleloopsthoughaListafterfirstfilteringtheresults.
Test02Filter.java

9 public class Test02Filter {


10
11
public static void main(String[] args) {

11
12
13
14
15

public static void main(String[] args) {


List<Person> pl = Person.createShortList();
SearchCriteria search = SearchCriteria.getInstance();

16
17
System.out.println("\n=== Western Pilot Phone List ===");
18
19
pl.stream().filter(search.getCriteria("allPilots"))
20
.forEach(Person::printWesternName);
21
22
23
System.out.println("\n=== Eastern Draftee Phone List ===");
24
25
pl.stream().filter(search.getCriteria("allDraftees"))
26
.forEach(Person::printEasternName);
27
28
}
29 }

ThefirstandlastloopsdemonstratehowtheListisfilteredbasedonthe
searchcriteria.Theoutputfromthelastlooplookslikefollowing:
=== Eastern Draftee Phone List ===
Name: Baker Bob
Age: 21 Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333
Name: Doe John
Age: 25 Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

GettingLazy
Thesefeaturesareuseful,butwhyaddthemtothecollectionsclasseswhen
thereisalreadyaperfectlygoodforloop?Bymovingiterationfeaturesintoa
library,itallowsthedevelopersofJavatodomorecodeoptimizations.To
explainfurther,acoupleoftermsneeddefinitions.
Laziness:Inprogramming,lazinessreferstoprocessingonlythe
objectsthatyouwanttoprocesswhenyouneedtoprocessthem.Inthe
previousexample,thelastloopis"lazy"becauseitloopsonlythrough
thetwoPersonobjectsleftaftertheListisfiltered.Thecodeshouldbe
moreefficientbecausethefinalprocessingstepoccursonlyonthe
selectedobjects.
Eagerness:Codethatperformsoperationsoneveryobjectinalistis
considered"eager".Forexample,anenhancedforloopthatiterates
throughanentirelisttoprocesstwoobjects,isconsideredamore
"eager"approach.
Bymakingloopingpartofthecollectionslibrary,codecanbebetteroptimized

Bymakingloopingpartofthecollectionslibrary,codecanbebetteroptimized
for"lazy"operationswhentheopportunityarises.Whenamoreeager
approachmakessense(forexample,computingasumoranaverage),eager
operationsarestillapplied.Thisapproachisamuchmoreefficientand
flexiblethanalwaysusingeageroperations.
ThestreamMethod
Inthepreviouscodeexample,noticethatthestreammethodiscalledbefore
filteringandloopingbegin.ThismethodtakesaCollectionasinputand
returnsajava.util.stream.Streaminterfaceastheoutput.AStream
representsasequenceofelementsonwhichvariousmethodscanbe
chained.Bydefault,onceelementsareconsumedtheyarenolonger
availablefromthestream.Therefore,achainofoperationscanoccuronly
onceonaparticularStream.Inaddition,aStreamcanbeserial(default)or
paralleldependingonthemethodcalled.Anexampleofaparallelstreamis
includedattheendofthissection.

MutationandResults
Aspreviouslymentioned,aStreamisdisposedofafteritsuse.Therefore,the
elementsinacollectioncannotbechangedormutatedwithaStream.
However,whatifyouwanttokeepelementsreturnedfromyourchained
operations?Youcansavethemtoanewcollection.Thefollowingcode
showshowtodojustthat.

Test03toList.java
10 public class Test03toList {
11
12
public static void main(String[] args) {
13
14
List<Person> pl = Person.createShortList();
15
16
SearchCriteria search = SearchCriteria.getInstance();
17
18
// Make a new list after filtering.
19
List<Person> pilotList = pl
20
.stream()
21
.filter(search.getCriteria("allPilots"))
22
.collect(Collectors.toList());
23
24
System.out.println("\n=== Western Pilot Phone List ===");
25
pilotList.forEach(Person::printWesternName);
26
27
}
28
29 }

Thecollectmethodiscalledwithoneparameter,theCollectorsclass.
TheCollectorsclassisabletoreturnaListorSetbasedontheresultsof
thestream.Theexampleshowshowtheresultofthestreamisassignedtoa

thestream.Theexampleshowshowtheresultofthestreamisassignedtoa
newListwhichisiteratedover.

Calculatingwithmap
Themapmethodiscommonlyusedwithfilter.Themethodtakesaproperty
fromaclassanddoessomethingwithit.Thefollowingexampledemonstrates
thisbyperformingcalculationsbasedonage.

Test04Map.java
10 public class Test04Map {
11
12
public static void main(String[] args) {
13
List<Person> pl = Person.createShortList();
14
15
SearchCriteria search = SearchCriteria.getInstance();
16
17
// Calc average age of pilots old style
18
System.out.println("== Calc Old Style ==");
19
int sum = 0;
20
int count = 0;
21
22
for (Person p:pl){
23
if (p.getAge() >= 23 && p.getAge() <= 65 ){
24
sum = sum + p.getAge();
25
count++;
26
}
27
}
28
29
long average = sum / count;
30
System.out.println("Total Ages: " + sum);
31
System.out.println("Average Age: " + average);
32
33
34
// Get sum of ages
35
System.out.println("\n== Calc New Style ==");
36
long totalAge = pl
37
.stream()
38
.filter(search.getCriteria("allPilots"))
39
.mapToInt(p -> p.getAge())
40
.sum();
41
42
// Get average of ages
43
OptionalDouble averageAge = pl
44
.parallelStream()
45
.filter(search.getCriteria("allPilots"))
46
.mapToDouble(p -> p.getAge())
47
.average();
48
49
System.out.println("Total Ages: " + totalAge);
50
System.out.println("Average Age: " +
averageAge.getAsDouble());
51
52
}
53
54 }

Andtheoutputfromtheclassis:

== Calc Old Style ==


Total Ages: 150
Average Age: 37
== Calc New Style ==
Total Ages: 150
Average Age: 37.5

Theprogramcalculatestheaverageageofpilotsinourlist.Thefirstloop
demonstratestheoldstyleofcalculatingthenumberbyusingaforloop.The
secondloopusesthemapmethodtogettheageofeachpersoninaserial
stream.NoticethattotalAgeisalong.ThemapmethodreturnsanIntSteam
object,whichcontainsasummethodthatreturnsalong.
Note:Tocomputetheaveragethesecondtime,calculatingthesumofages
isunnecessary.However,itisinstructivetoshowanexamplewiththesum
method.
Thelastloopcomputestheaverageagefromthestream.Noticethatthe
parallelStreammethodisusedtogetaparallelstreamsothatthevalues
canbecomputedconcurrently.Thereturntypeisabitdifferenthereaswell.

Resources
TheNetBeansprojectwithsourcecodefortheexamplesisinthefollowing
zipfile.
LambdaCollectionExamples.zip

Summary

Inthistutorial,youlearnedhowtouse:
AnonymousinnerclassesinJava.
LambdaexpressionstoreplaceanonymousinnerclassesinJavaSE8.
Thecorrectsyntaxforlambdaexpressions.
APredicateinterfacetoperformsearchesonalist.
AFunctioninterfacetoprocessanobjectandproduceadifferenttype
ofobject.
NewfeaturesaddedtoCollectionsinJavaSE8thatsupportlambda
expressions.

Resources
ForfurtherinformationonJavaSE8andlambdaexpressions,seethe
following:
Java8
ProjectLambda

ProjectLambda
StateoftheLambda
StateoftheLambdaCollections
JumpStartingLambdaJavaOne2012(YouTube)
TolearnmoreaboutJavaandrelatedtopicscheckouttheOracle
LearningLibrary.

Credits
LeadCurriculumDeveloper:MichaelWilliams
QA:JuanQuesadaNunez

You might also like