Modern Java A Guide To 8 Java8
User Manual:
Open the PDF directly: View PDF
.
Page Count: 90
- Introduction
- Modern Java - A Guide to Java 8
- Java 8 Stream Tutorial
- Java 8 Nashorn Tutorial
- Java 8 Concurrency Tutorial: Threads and Executors
- Java 8 Concurrency Tutorial: Synchronization and Locks
- Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap
- Java 8 API by Example: Strings, Numbers, Math and Files
- Avoiding Null Checks in Java 8
- Fixing Java 8 Stream Gotchas with IntelliJ IDEA
- Using Backbone.js with Nashorn


0
1
2
3
4
5
6
7
8
9
10
TableofContents
Introduction
ModernJava-AGuidetoJava8
Java8StreamTutorial
Java8NashornTutorial
Java8ConcurrencyTutorial:ThreadsandExecutors
Java8ConcurrencyTutorial:SynchronizationandLocks
Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap
Java8APIbyExample:Strings,Numbers,MathandFiles
AvoidingNullChecksinJava8
FixingJava8StreamGotchaswithIntelliJIDEA
UsingBackbone.jswithNashorn
ModernJava-AGuidetoJava8
2

ModernJava-AGuidetoJava8
“Javaisstillnotdead—andpeoplearestartingtofigurethatout.”
WelcometomyintroductiontoJava8.Thistutorialguidesyoustepbystepthroughallnew
languagefeatures.Backedbyshortandsimplecodesamplesyou'lllearnhowtousedefault
interfacemethods,lambdaexpressions,methodreferencesandrepeatableannotations.At
theendofthearticleyou'llbefamiliarwiththemostrecentAPIchangeslikestreams,
functionalinterfaces,mapextensionsandthenewDateAPI.Nowallsoftext,justabunch
ofcommentedcodesnippets.Enjoy!
Thisarticlewasoriginallypostedonmyblog.YoushouldfollowmeonTwitter.
TableofContents
DefaultMethodsforInterfaces
Lambdaexpressions
FunctionalInterfaces
MethodandConstructorReferences
LambdaScopes
Accessinglocalvariables
Accessingfieldsandstaticvariables
AccessingDefaultInterfaceMethods
Built-inFunctionalInterfaces
Predicates
Functions
Suppliers
Consumers
Comparators
Optionals
Streams
Filter
Sorted
Map
Match
Count
Reduce
ParallelStreams
SequentialSort
ModernJava-AGuidetoJava8
4ModernJava-AGuidetoJava8

ParallelSort
Maps
DateAPI
Clock
Timezones
LocalTime
LocalDate
LocalDateTime
Annotations
Wheretogofromhere?
DefaultMethodsforInterfaces
Java8enablesustoaddnon-abstractmethodimplementationstointerfacesbyutilizingthe
defaultkeyword.Thisfeatureisalsoknownasvirtualextensionmethods.
Hereisourfirstexample:
interfaceFormula{
doublecalculate(inta);
defaultdoublesqrt(inta){
returnMath.sqrt(a);
}
}
Besidestheabstractmethod calculatetheinterface Formulaalsodefinesthedefault
method sqrt.Concreteclassesonlyhavetoimplementtheabstractmethod calculate.
Thedefaultmethod sqrtcanbeusedoutofthebox.
Formulaformula=newFormula(){
@Override
publicdoublecalculate(inta){
returnsqrt(a*100);
}
};
formula.calculate(100);//100.0
formula.sqrt(16);//4.0
Theformulaisimplementedasananonymousobject.Thecodeisquiteverbose:6linesof
codeforsuchasimplecalculationof sqrt(a*100).Aswe'llseeinthenextsection,there's
amuchnicerwayofimplementingsinglemethodobjectsinJava8.
Lambdaexpressions
ModernJava-AGuidetoJava8
5ModernJava-AGuidetoJava8

Let'sstartwithasimpleexampleofhowtosortalistofstringsinpriorversionsofJava:
List<String>names=Arrays.asList("peter","anna","mike","xenia");
Collections.sort(names,newComparator<String>(){
@Override
publicintcompare(Stringa,Stringb){
returnb.compareTo(a);
}
});
Thestaticutilitymethod Collections.sortacceptsalistandacomparatorinordertosort
theelementsofthegivenlist.Youoftenfindyourselfcreatinganonymouscomparatorsand
passthemtothesortmethod.
Insteadofcreatinganonymousobjectsalldaylong,Java8comeswithamuchshorter
syntax,lambdaexpressions:
Collections.sort(names,(Stringa,Stringb)->{
returnb.compareTo(a);
});
Asyoucanseethecodeismuchshorterandeasiertoread.Butitgetsevenshorter:
Collections.sort(names,(Stringa,Stringb)->b.compareTo(a));
Foronelinemethodbodiesyoucanskipboththebraces {}andthe returnkeyword.But
itgetsevenshorter:
names.sort((a,b)->b.compareTo(a));
Listnowhasa sortmethod.Alsothejavacompilerisawareoftheparametertypessoyou
canskipthemaswell.Let'sdivedeeperintohowlambdaexpressionscanbeusedinthe
wild.
FunctionalInterfaces
HowdoeslambdaexpressionsfitintoJava'stypesystem?Eachlambdacorrespondstoa
giventype,specifiedbyaninterface.Asocalledfunctionalinterfacemustcontainexactly
oneabstractmethoddeclaration.Eachlambdaexpressionofthattypewillbematchedto
thisabstractmethod.Sincedefaultmethodsarenotabstractyou'refreetoadddefault
methodstoyourfunctionalinterface.
ModernJava-AGuidetoJava8
6ModernJava-AGuidetoJava8

Wecanusearbitraryinterfacesaslambdaexpressionsaslongastheinterfaceonly
containsoneabstractmethod.Toensurethatyourinterfacemeettherequirements,you
shouldaddthe @FunctionalInterfaceannotation.Thecompilerisawareofthisannotation
andthrowsacompilererrorassoonasyoutrytoaddasecondabstractmethoddeclaration
totheinterface.
Example:
@FunctionalInterface
interfaceConverter<F,T>{
Tconvert(Ffrom);
}
Converter<String,Integer>converter=(from)->Integer.valueOf(from);
Integerconverted=converter.convert("123");
System.out.println(converted);//123
Keepinmindthatthecodeisalsovalidifthe @FunctionalInterfaceannotationwouldbe
omitted.
MethodandConstructorReferences
Theaboveexamplecodecanbefurthersimplifiedbyutilizingstaticmethodreferences:
Converter<String,Integer>converter=Integer::valueOf;
Integerconverted=converter.convert("123");
System.out.println(converted);//123
Java8enablesyoutopassreferencesofmethodsorconstructorsviathe ::keyword.The
aboveexampleshowshowtoreferenceastaticmethod.Butwecanalsoreferenceobject
methods:
classSomething{
StringstartsWith(Strings){
returnString.valueOf(s.charAt(0));
}
}
Somethingsomething=newSomething();
Converter<String,String>converter=something::startsWith;
Stringconverted=converter.convert("Java");
System.out.println(converted);//"J"
Let'sseehowthe ::keywordworksforconstructors.Firstwedefineanexamplebeanwith
differentconstructors:
ModernJava-AGuidetoJava8
7ModernJava-AGuidetoJava8

classPerson{
StringfirstName;
StringlastName;
Person(){}
Person(StringfirstName,StringlastName){
this.firstName=firstName;
this.lastName=lastName;
}
}
Nextwespecifyapersonfactoryinterfacetobeusedforcreatingnewpersons:
interfacePersonFactory<PextendsPerson>{
Pcreate(StringfirstName,StringlastName);
}
Insteadofimplementingthefactorymanually,weglueeverythingtogetherviaconstructor
references:
PersonFactory<Person>personFactory=Person::new;
Personperson=personFactory.create("Peter","Parker");
WecreateareferencetothePersonconstructorvia Person::new.TheJavacompiler
automaticallychoosestherightconstructorbymatchingthesignatureof
PersonFactory.create.
LambdaScopes
Accessingouterscopevariablesfromlambdaexpressionsisverysimilartoanonymous
objects.Youcanaccessfinalvariablesfromthelocalouterscopeaswellasinstancefields
andstaticvariables.
Accessinglocalvariables
Wecanreadfinallocalvariablesfromtheouterscopeoflambdaexpressions:
finalintnum=1;
Converter<Integer,String>stringConverter=
(from)->String.valueOf(from+num);
stringConverter.convert(2);//3
Butdifferenttoanonymousobjectsthevariable numdoesnothavetobedeclaredfinal.
Thiscodeisalsovalid:
ModernJava-AGuidetoJava8
8ModernJava-AGuidetoJava8

intnum=1;
Converter<Integer,String>stringConverter=
(from)->String.valueOf(from+num);
stringConverter.convert(2);//3
However nummustbeimplicitlyfinalforthecodetocompile.Thefollowingcodedoesnot
compile:
intnum=1;
Converter<Integer,String>stringConverter=
(from)->String.valueOf(from+num);
num=3;
Writingto numfromwithinthelambdaexpressionisalsoprohibited.
Accessingfieldsandstaticvariables
Incontrasttolocalvariables,wehavebothreadandwriteaccesstoinstancefieldsand
staticvariablesfromwithinlambdaexpressions.Thisbehaviouriswellknownfrom
anonymousobjects.
classLambda4{
staticintouterStaticNum;
intouterNum;
voidtestScopes(){
Converter<Integer,String>stringConverter1=(from)->{
outerNum=23;
returnString.valueOf(from);
};
Converter<Integer,String>stringConverter2=(from)->{
outerStaticNum=72;
returnString.valueOf(from);
};
}
}
AccessingDefaultInterfaceMethods
Remembertheformulaexamplefromthefirstsection?Interface Formuladefinesadefault
method sqrtwhichcanbeaccessedfromeachformulainstanceincludinganonymous
objects.Thisdoesnotworkwithlambdaexpressions.
Defaultmethodscannotbeaccessedfromwithinlambdaexpressions.Thefollowingcode
doesnotcompile:
Formulaformula=(a)->sqrt(a*100);
ModernJava-AGuidetoJava8
9ModernJava-AGuidetoJava8

Built-inFunctionalInterfaces
TheJDK1.8APIcontainsmanybuilt-infunctionalinterfaces.Someofthemarewellknown
fromolderversionsofJavalike Comparatoror Runnable.Thoseexistinginterfacesare
extendedtoenableLambdasupportviathe @FunctionalInterfaceannotation.
ButtheJava8APIisalsofullofnewfunctionalinterfacestomakeyourlifeeasier.Someof
thosenewinterfacesarewellknownfromtheGoogleGuavalibrary.Evenifyou'refamiliar
withthislibraryyoushouldkeepacloseeyeonhowthoseinterfacesareextendedbysome
usefulmethodextensions.
Predicates
Predicatesareboolean-valuedfunctionsofoneargument.Theinterfacecontainsvarious
defaultmethodsforcomposingpredicatestocomplexlogicalterms(and,or,negate)
Predicate<String>predicate=(s)->s.length()>0;
predicate.test("foo");//true
predicate.negate().test("foo");//false
Predicate<Boolean>nonNull=Objects::nonNull;
Predicate<Boolean>isNull=Objects::isNull;
Predicate<String>isEmpty=String::isEmpty;
Predicate<String>isNotEmpty=isEmpty.negate();
Functions
Functionsacceptoneargumentandproducearesult.Defaultmethodscanbeusedtochain
multiplefunctionstogether(compose,andThen).
Function<String,Integer>toInteger=Integer::valueOf;
Function<String,String>backToString=toInteger.andThen(String::valueOf);
backToString.apply("123");//"123"
Suppliers
Suppliersproducearesultofagivengenerictype.UnlikeFunctions,Suppliersdon'taccept
arguments.
Supplier<Person>personSupplier=Person::new;
personSupplier.get();//newPerson
Consumers
ModernJava-AGuidetoJava8
10ModernJava-AGuidetoJava8

Consumersrepresentoperationstobeperformedonasingleinputargument.
Consumer<Person>greeter=(p)->System.out.println("Hello,"+p.firstName);
greeter.accept(newPerson("Luke","Skywalker"));
Comparators
ComparatorsarewellknownfromolderversionsofJava.Java8addsvariousdefault
methodstotheinterface.
Comparator<Person>comparator=(p1,p2)->p1.firstName.compareTo(p2.firstName);
Personp1=newPerson("John","Doe");
Personp2=newPerson("Alice","Wonderland");
comparator.compare(p1,p2);//>0
comparator.reversed().compare(p1,p2);//<0
Optionals
Optionalsarenotfunctionalinterfaces,butniftyutilitiestoprevent NullPointerException.It's
animportantconceptforthenextsection,solet'shaveaquicklookathowOptionalswork.
Optionalisasimplecontainerforavaluewhichmaybenullornon-null.Thinkofamethod
whichmayreturnanon-nullresultbutsometimesreturnnothing.Insteadofreturning null
youreturnan OptionalinJava8.
Optional<String>optional=Optional.of("bam");
optional.isPresent();//true
optional.get();//"bam"
optional.orElse("fallback");//"bam"
optional.ifPresent((s)->System.out.println(s.charAt(0)));//"b"
Streams
A java.util.Streamrepresentsasequenceofelementsonwhichoneormoreoperations
canbeperformed.Streamoperationsareeitherintermediateorterminal.Whileterminal
operationsreturnaresultofacertaintype,intermediateoperationsreturnthestreamitself
soyoucanchainmultiplemethodcallsinarow.Streamsarecreatedonasource,e.g.a
java.util.Collectionlikelistsorsets(mapsarenotsupported).Streamoperationscan
eitherbeexecutedsequentiallyorparallely.
Streamsareextremelypowerful,soIwroteaseparateJava8StreamsTutorial.You
shouldalsocheckoutStream.js,aJavaScriptportoftheJava8StreamsAPI.
ModernJava-AGuidetoJava8
11ModernJava-AGuidetoJava8

Let'sfirstlookhowsequentialstreamswork.Firstwecreateasamplesourceinformofalist
ofstrings:
List<String>stringCollection=newArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
CollectionsinJava8areextendedsoyoucansimplycreatestreamseitherbycalling
Collection.stream()or Collection.parallelStream().Thefollowingsectionsexplainthe
mostcommonstreamoperations.
Filter
Filteracceptsapredicatetofilterallelementsofthestream.Thisoperationisintermediate
whichenablesustocallanotherstreamoperation( forEach)ontheresult.ForEachaccepts
aconsumertobeexecutedforeachelementinthefilteredstream.ForEachisaterminal
operation.It's void,sowecannotcallanotherstreamoperation.
stringCollection
.stream()
.filter((s)->s.startsWith("a"))
.forEach(System.out::println);
//"aaa2","aaa1"
Sorted
Sortedisanintermediateoperationwhichreturnsasortedviewofthestream.Theelements
aresortedinnaturalorderunlessyoupassacustom Comparator.
stringCollection
.stream()
.sorted()
.filter((s)->s.startsWith("a"))
.forEach(System.out::println);
//"aaa1","aaa2"
Keepinmindthat sorteddoesonlycreateasortedviewofthestreamwithoutmanipulating
theorderingofthebackedcollection.Theorderingof stringCollectionisuntouched:
System.out.println(stringCollection);
//ddd2,aaa2,bbb1,aaa1,bbb3,ccc,bbb2,ddd1
ModernJava-AGuidetoJava8
12ModernJava-AGuidetoJava8

Map
Theintermediateoperation mapconvertseachelementintoanotherobjectviathegiven
function.Thefollowingexampleconvertseachstringintoanupper-casedstring.Butyoucan
alsouse maptotransformeachobjectintoanothertype.Thegenerictypeoftheresulting
streamdependsonthegenerictypeofthefunctionyoupassto map.
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a,b)->b.compareTo(a))
.forEach(System.out::println);
//"DDD2","DDD1","CCC","BBB3","BBB2","AAA2","AAA1"
Match
Variousmatchingoperationscanbeusedtocheckwhetheracertainpredicatematchesthe
stream.Allofthoseoperationsareterminalandreturnabooleanresult.
booleananyStartsWithA=
stringCollection
.stream()
.anyMatch((s)->s.startsWith("a"));
System.out.println(anyStartsWithA);//true
booleanallStartsWithA=
stringCollection
.stream()
.allMatch((s)->s.startsWith("a"));
System.out.println(allStartsWithA);//false
booleannoneStartsWithZ=
stringCollection
.stream()
.noneMatch((s)->s.startsWith("z"));
System.out.println(noneStartsWithZ);//true
Count
Countisaterminaloperationreturningthenumberofelementsinthestreamasa long.
longstartsWithB=
stringCollection
.stream()
.filter((s)->s.startsWith("b"))
.count();
System.out.println(startsWithB);//3
Reduce
ModernJava-AGuidetoJava8
13ModernJava-AGuidetoJava8

Thisterminaloperationperformsareductionontheelementsofthestreamwiththegiven
function.Theresultisan Optionalholdingthereducedvalue.
Optional<String>reduced=
stringCollection
.stream()
.sorted()
.reduce((s1,s2)->s1+"#"+s2);
reduced.ifPresent(System.out::println);
//"aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
ParallelStreams
Asmentionedabovestreamscanbeeithersequentialorparallel.Operationsonsequential
streamsareperformedonasinglethreadwhileoperationsonparallelstreamsare
performedconcurrentlyonmultiplethreads.
Thefollowingexampledemonstrateshoweasyitistoincreasetheperformancebyusing
parallelstreams.
Firstwecreatealargelistofuniqueelements:
intmax=1000000;
List<String>values=newArrayList<>(max);
for(inti=0;i<max;i++){
UUIDuuid=UUID.randomUUID();
values.add(uuid.toString());
}
Nowwemeasurethetimeittakestosortastreamofthiscollection.
SequentialSort
longt0=System.nanoTime();
longcount=values.stream().sorted().count();
System.out.println(count);
longt1=System.nanoTime();
longmillis=TimeUnit.NANOSECONDS.toMillis(t1-t0);
System.out.println(String.format("sequentialsorttook:%dms",millis));
//sequentialsorttook:899ms
ParallelSort
ModernJava-AGuidetoJava8
14ModernJava-AGuidetoJava8

longt0=System.nanoTime();
longcount=values.parallelStream().sorted().count();
System.out.println(count);
longt1=System.nanoTime();
longmillis=TimeUnit.NANOSECONDS.toMillis(t1-t0);
System.out.println(String.format("parallelsorttook:%dms",millis));
//parallelsorttook:472ms
Asyoucanseebothcodesnippetsarealmostidenticalbuttheparallelsortisroughly50%
faster.Allyouhavetodoischange stream()to parallelStream().
Maps
Asalreadymentionedmapsdonotdirectlysupportstreams.There'sno stream()method
availableonthe Mapinterfaceitself,howeveryoucancreatespecializedstreamsuponthe
keys,valuesorentriesofamapvia map.keySet().stream(), map.values().stream()and
map.entrySet().stream().
Furthermoremapssupportvariousnewandusefulmethodsfordoingcommontasks.
Map<Integer,String>map=newHashMap<>();
for(inti=0;i<10;i++){
map.putIfAbsent(i,"val"+i);
}
map.forEach((id,val)->System.out.println(val));
Theabovecodeshouldbeself-explaining: putIfAbsentpreventsusfromwritingadditional
ifnullchecks; forEachacceptsaconsumertoperformoperationsforeachvalueofthe
map.
Thisexampleshowshowtocomputecodeonthemapbyutilizingfunctions:
map.computeIfPresent(3,(num,val)->val+num);
map.get(3);//val33
map.computeIfPresent(9,(num,val)->null);
map.containsKey(9);//false
map.computeIfAbsent(23,num->"val"+num);
map.containsKey(23);//true
map.computeIfAbsent(3,num->"bam");
map.get(3);//val33
Next,welearnhowtoremoveentriesforagivenkey,onlyifit'scurrentlymappedtoagiven
value:
ModernJava-AGuidetoJava8
15ModernJava-AGuidetoJava8

map.remove(3,"val3");
map.get(3);//val33
map.remove(3,"val33");
map.get(3);//null
Anotherhelpfulmethod:
map.getOrDefault(42,"notfound");//notfound
Mergingentriesofamapisquiteeasy:
map.merge(9,"val9",(value,newValue)->value.concat(newValue));
map.get(9);//val9
map.merge(9,"concat",(value,newValue)->value.concat(newValue));
map.get(9);//val9concat
Mergeeitherputthekey/valueintothemapifnoentryforthekeyexists,orthemerging
functionwillbecalledtochangetheexistingvalue.
DateAPI
Java8containsabrandnewdateandtimeAPIunderthepackage java.time.Thenew
DateAPIiscomparablewiththeJoda-Timelibrary,howeverit'snotthesame.Thefollowing
examplescoverthemostimportantpartsofthisnewAPI.
Clock
Clockprovidesaccesstothecurrentdateandtime.Clocksareawareofatimezoneand
maybeusedinsteadof System.currentTimeMillis()toretrievethecurrenttimein
millisecondssinceUnixEPOCH.Suchaninstantaneouspointonthetime-lineisalso
representedbytheclass Instant.Instantscanbeusedtocreatelegacy java.util.Date
objects.
Clockclock=Clock.systemDefaultZone();
longmillis=clock.millis();
Instantinstant=clock.instant();
DatelegacyDate=Date.from(instant);//legacyjava.util.Date
Timezones
ModernJava-AGuidetoJava8
16ModernJava-AGuidetoJava8

Timezonesarerepresentedbya ZoneId.Theycaneasilybeaccessedviastaticfactory
methods.Timezonesdefinetheoffsetswhichareimportanttoconvertbetweeninstantsand
localdatesandtimes.
System.out.println(ZoneId.getAvailableZoneIds());
//printsallavailabletimezoneids
ZoneIdzone1=ZoneId.of("Europe/Berlin");
ZoneIdzone2=ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());
//ZoneRules[currentStandardOffset=+01:00]
//ZoneRules[currentStandardOffset=-03:00]
LocalTime
LocalTimerepresentsatimewithoutatimezone,e.g.10pmor17:30:15.Thefollowing
examplecreatestwolocaltimesforthetimezonesdefinedabove.Thenwecompareboth
timesandcalculatethedifferenceinhoursandminutesbetweenbothtimes.
LocalTimenow1=LocalTime.now(zone1);
LocalTimenow2=LocalTime.now(zone2);
System.out.println(now1.isBefore(now2));//false
longhoursBetween=ChronoUnit.HOURS.between(now1,now2);
longminutesBetween=ChronoUnit.MINUTES.between(now1,now2);
System.out.println(hoursBetween);//-3
System.out.println(minutesBetween);//-239
LocalTimecomeswithvariousfactorymethodstosimplifythecreationofnewinstances,
includingparsingoftimestrings.
LocalTimelate=LocalTime.of(23,59,59);
System.out.println(late);//23:59:59
DateTimeFormattergermanFormatter=
DateTimeFormatter
.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(Locale.GERMAN);
LocalTimeleetTime=LocalTime.parse("13:37",germanFormatter);
System.out.println(leetTime);//13:37
LocalDate
LocalDaterepresentsadistinctdate,e.g.2014-03-11.It'simmutableandworksexactly
analogtoLocalTime.Thesampledemonstrateshowtocalculatenewdatesbyaddingor
subtractingdays,monthsoryears.Keepinmindthateachmanipulationreturnsanew
instance.
ModernJava-AGuidetoJava8
17ModernJava-AGuidetoJava8

LocalDatetoday=LocalDate.now();
LocalDatetomorrow=today.plus(1,ChronoUnit.DAYS);
LocalDateyesterday=tomorrow.minusDays(2);
LocalDateindependenceDay=LocalDate.of(2014,Month.JULY,4);
DayOfWeekdayOfWeek=independenceDay.getDayOfWeek();
System.out.println(dayOfWeek);//FRIDAY
ParsingaLocalDatefromastringisjustassimpleasparsingaLocalTime:
DateTimeFormattergermanFormatter=
DateTimeFormatter
.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.GERMAN);
LocalDatexmas=LocalDate.parse("24.12.2014",germanFormatter);
System.out.println(xmas);//2014-12-24
LocalDateTime
LocalDateTimerepresentsadate-time.Itcombinesdateandtimeasseenintheabove
sectionsintooneinstance. LocalDateTimeisimmutableandworkssimilartoLocalTimeand
LocalDate.Wecanutilizemethodsforretrievingcertainfieldsfromadate-time:
LocalDateTimesylvester=LocalDateTime.of(2014,Month.DECEMBER,31,23,59,59);
DayOfWeekdayOfWeek=sylvester.getDayOfWeek();
System.out.println(dayOfWeek);//WEDNESDAY
Monthmonth=sylvester.getMonth();
System.out.println(month);//DECEMBER
longminuteOfDay=sylvester.getLong(ChronoField.MINUTE_OF_DAY);
System.out.println(minuteOfDay);//1439
Withtheadditionalinformationofatimezoneitcanbeconvertedtoaninstant.Instantscan
easilybeconvertedtolegacydatesoftype java.util.Date.
Instantinstant=sylvester
.atZone(ZoneId.systemDefault())
.toInstant();
DatelegacyDate=Date.from(instant);
System.out.println(legacyDate);//WedDec3123:59:59CET2014
Formattingdate-timesworksjustlikeformattingdatesortimes.Insteadofusingpre-defined
formatswecancreateformattersfromcustompatterns.
ModernJava-AGuidetoJava8
18ModernJava-AGuidetoJava8

DateTimeFormatterformatter=
DateTimeFormatter
.ofPattern("MMMdd,yyyy-HH:mm");
LocalDateTimeparsed=LocalDateTime.parse("Nov03,2014-07:13",formatter);
Stringstring=formatter.format(parsed);
System.out.println(string);//Nov03,2014-07:13
Unlike java.text.NumberFormatthenew DateTimeFormatterisimmutableandthread-safe.
Fordetailsonthepatternsyntaxreadhere.
Annotations
AnnotationsinJava8arerepeatable.Let'sdivedirectlyintoanexampletofigurethatout.
First,wedefineawrapperannotationwhichholdsanarrayoftheactualannotations:
@interfaceHints{
Hint[]value();
}
@Repeatable(Hints.class)
@interfaceHint{
Stringvalue();
}
Java8enablesustousemultipleannotationsofthesametypebydeclaringtheannotation
@Repeatable.
Variant1:Usingthecontainerannotation(oldschool)
@Hints({@Hint("hint1"),@Hint("hint2")})
classPerson{}
Variant2:Usingrepeatableannotations(newschool)
@Hint("hint1")
@Hint("hint2")
classPerson{}
Usingvariant2thejavacompilerimplicitlysetsupthe @Hintsannotationunderthehood.
That'simportantforreadingannotationinformationviareflection.
ModernJava-AGuidetoJava8
19ModernJava-AGuidetoJava8

Hinthint=Person.class.getAnnotation(Hint.class);
System.out.println(hint);//null
Hintshints1=Person.class.getAnnotation(Hints.class);
System.out.println(hints1.value().length);//2
Hint[]hints2=Person.class.getAnnotationsByType(Hint.class);
System.out.println(hints2.length);//2
Althoughweneverdeclaredthe @Hintsannotationonthe Personclass,it'sstillreadable
via getAnnotation(Hints.class).However,themoreconvenientmethodis
getAnnotationsByTypewhichgrantsdirectaccesstoallannotated @Hintannotations.
FurthermoretheusageofannotationsinJava8isexpandedtotwonewtargets:
@Target({ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})
@interfaceMyAnnotation{}
Wheretogofromhere?
MyprogrammingguidetoJava8endshere.Ifyouwanttolearnmoreaboutallthenew
classesandfeaturesoftheJDK8API,checkoutmyJDK8APIExplorer.Ithelpsyou
figuringoutallthenewclassesandhiddengemsofJDK8,like Arrays.parallelSort,
StampedLockand CompletableFuture-justtonameafew.
I'vealsopublishedabunchoffollow-uparticlesonmyblogthatmightbeinterestingtoyou:
Java8StreamTutorial
Java8NashornTutorial
Java8ConcurrencyTutorial:ThreadsandExecutors
Java8ConcurrencyTutorial:SynchronizationandLocks
Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap
Java8APIbyExample:Strings,Numbers,MathandFiles
AvoidNullChecksinJava8
FixingJava8StreamGotchaswithIntelliJIDEA
UsingBackbone.jswithJava8Nashorn
YoushouldfollowmeonTwitter.Thanksforreading!
ModernJava-AGuidetoJava8
20ModernJava-AGuidetoJava8

Java8StreamTutorial
July31,2014
Thisexample-driventutorialgivesanin-depthoverviewaboutJava8streams.WhenIfirst
readaboutthe StreamAPI,Iwasconfusedaboutthenamesinceitsoundssimilarto
InputStreamand OutputStreamfromJavaI/O.ButJava8streamsareacompletely
differentthing.StreamsareMonads,thusplayingabigpartinbringingfunctional
programmingtoJava:
>Infunctionalprogramming,amonadisastructurethatrepresentscomputationsdefinedas
sequencesofsteps.Atypewithamonadstructuredefineswhatitmeanstochain
operations,ornestfunctionsofthattypetogether.
ThisguideteachesyouhowtoworkwithJava8streamsandhowtousethedifferentkindof
availablestreamoperations.You'lllearnabouttheprocessingorderandhowtheorderingof
streamoperationsaffectruntimeperformance.Themorepowerfulstreamoperations
reduce, collectand flatMaparecoveredindetail.Thetutorialendswithanin-depth
lookatparallelstreams.
Ifyou'renotyetfamiliarwithJava8lambdaexpressions,functionalinterfacesandmethod
references,youprobablywanttoreadmyJava8Tutorialfirstbeforestartingwiththis
tutorial.
UPDATE-I'mcurrentlyworkingonaJavaScriptimplementationoftheJava8StreamsAPI
forthebrowser.IfI'vedrawnyourinterestcheckoutStream.jsonGitHub.YourFeedbackis
highlyappreciated.
Howstreamswork
Astreamrepresentsasequenceofelementsandsupportsdifferentkindofoperationsto
performcomputationsuponthoseelements:
List<String>myList=
Arrays.asList("a1","a2","b1","c2","c1");
myList
.stream()
.filter(s->s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
//C1
//C2
ModernJava-AGuidetoJava8
21Java8StreamTutorial

Streamoperationsareeitherintermediateorterminal.Intermediateoperationsreturna
streamsowecanchainmultipleintermediateoperationswithoutusingsemicolons.Terminal
operationsareeithervoidorreturnanon-streamresult.Intheaboveexample filter,
mapand sortedareintermediateoperationswhereas forEachisaterminaloperation.
ForafulllistofallavailablestreamoperationsseetheStreamJavadoc.Suchachainof
streamoperationsasseenintheexampleaboveisalsoknownasoperationpipeline.
Moststreamoperationsacceptsomekindoflambdaexpressionparameter,afunctional
interfacespecifyingtheexactbehavioroftheoperation.Mostofthoseoperationsmustbe
bothnon-interferingandstateless.Whatdoesthatmean?
Afunctionisnon-interferingwhenitdoesnotmodifytheunderlyingdatasourceofthe
stream,e.g.intheaboveexamplenolambdaexpressiondoesmodify myListbyaddingor
removingelementsfromthecollection.
Afunctionisstatelesswhentheexecutionoftheoperationisdeterministic,e.g.intheabove
examplenolambdaexpressiondependsonanymutablevariablesorstatesfromtheouter
scopewhichmightchangeduringexecution.
Differentkindofstreams
Streamscanbecreatedfromvariousdatasources,especiallycollections.ListsandSets
supportnewmethods stream()and parallelStream()toeithercreateasequentialora
parallelstream.Parallelstreamsarecapableofoperatingonmultiplethreadsandwillbe
coveredinalatersectionofthistutorial.Wefocusonsequentialstreamsfornow:
Arrays.asList("a1","a2","a3")
.stream()
.findFirst()
.ifPresent(System.out::println);//a1
Callingthemethod stream()onalistofobjectsreturnsaregularobjectstream.Butwe
don'thavetocreatecollectionsinordertoworkwithstreamsasweseeinthenextcode
sample:
Stream.of("a1","a2","a3")
.findFirst()
.ifPresent(System.out::println);//a1
Justuse Stream.of()tocreateastreamfromabunchofobjectreferences.
BesidesregularobjectstreamsJava8shipswithspecialkindsofstreamsforworkingwith
theprimitivedatatypes int, longand double.Asyoumighthaveguessedit's
IntStream, LongStreamand DoubleStream.
ModernJava-AGuidetoJava8
22Java8StreamTutorial

IntStreamscanreplacetheregularfor-looputilizing IntStream.range():
IntStream.range(1,4)
.forEach(System.out::println);
//1
//2
//3
Allthoseprimitivestreamsworkjustlikeregularobjectstreamswiththefollowing
differences:Primitivestreamsusespecializedlambdaexpressions,e.g. IntFunction
insteadof Functionor IntPredicateinsteadof Predicate.Andprimitivestreamssupport
theadditionalterminalaggregateoperations sum()and average():
Arrays.stream(newint[]{1,2,3})
.map(n->2*n+1)
.average()
.ifPresent(System.out::println);//5.0
Sometimesit'susefultotransformaregularobjectstreamtoaprimitivestreamorvice
versa.Forthatpurposeobjectstreamssupportthespecialmappingoperations mapToInt(),
mapToLong()and mapToDouble:
Stream.of("a1","a2","a3")
.map(s->s.substring(1))
.mapToInt(Integer::parseInt)
.max()
.ifPresent(System.out::println);//3
Primitivestreamscanbetransformedtoobjectstreamsvia mapToObj():
IntStream.range(1,4)
.mapToObj(i->"a"+i)
.forEach(System.out::println);
//a1
//a2
//a3
Here'sacombinedexample:thestreamofdoublesisfirstmappedtoanintstreamandthan
mappedtoanobjectstreamofstrings:
Stream.of(1.0,2.0,3.0)
.mapToInt(Double::intValue)
.mapToObj(i->"a"+i)
.forEach(System.out::println);
//a1
//a2
//a3
ModernJava-AGuidetoJava8
23Java8StreamTutorial

ProcessingOrder
Nowthatwe'velearnedhowtocreateandworkwithdifferentkindsofstreams,let'sdive
deeperintohowstreamoperationsareprocessedunderthehood.
Animportantcharacteristicofintermediateoperationsislaziness.Lookatthissamplewhere
aterminaloperationismissing:
Stream.of("d2","a2","b1","b3","c")
.filter(s->{
System.out.println("filter:"+s);
returntrue;
});
Whenexecutingthiscodesnippet,nothingisprintedtotheconsole.Thatisbecause
intermediateoperationswillonlybeexecutedwhenaterminaloperationispresent.
Let'sextendtheaboveexamplebytheterminaloperation forEach:
Stream.of("d2","a2","b1","b3","c")
.filter(s->{
System.out.println("filter:"+s);
returntrue;
})
.forEach(s->System.out.println("forEach:"+s));
Executingthiscodesnippetresultsinthedesiredoutputontheconsole:
filter:d2
forEach:d2
filter:a2
forEach:a2
filter:b1
forEach:b1
filter:b3
forEach:b3
filter:c
forEach:c
Theorderoftheresultmightbesurprising.Anaiveapproachwouldbetoexecutethe
operationshorizontallyoneafteranotheronallelementsofthestream.Butinsteadeach
elementmovesalongthechainvertically.Thefirststring"d2"passes filterthen
forEach,onlythenthesecondstring"a2"isprocessed.
Thisbehaviorcanreducetheactualnumberofoperationsperformedoneachelement,as
weseeinthenextexample:
ModernJava-AGuidetoJava8
24Java8StreamTutorial

Stream.of("d2","a2","b1","b3","c")
.map(s->{
System.out.println("map:"+s);
returns.toUpperCase();
})
.anyMatch(s->{
System.out.println("anyMatch:"+s);
returns.startsWith("A");
});
//map:d2
//anyMatch:D2
//map:a2
//anyMatch:A2
Theoperation anyMatchreturns trueassoonasthepredicateappliestothegiveninput
element.Thisistrueforthesecondelementpassed"A2".Duetotheverticalexecutionof
thestreamchain, maphasonlytobeexecutedtwiceinthiscase.Soinsteadofmappingall
elementsofthestream, mapwillbecalledasfewaspossible.
Whyordermatters
Thenextexampleconsistsoftwointermediateoperations mapand filterandthe
terminaloperation forEach.Let'sonceagaininspecthowthoseoperationsarebeing
executed:
Stream.of("d2","a2","b1","b3","c")
.map(s->{
System.out.println("map:"+s);
returns.toUpperCase();
})
.filter(s->{
System.out.println("filter:"+s);
returns.startsWith("A");
})
.forEach(s->System.out.println("forEach:"+s));
//map:d2
//filter:D2
//map:a2
//filter:A2
//forEach:A2
//map:b1
//filter:B1
//map:b3
//filter:B3
//map:c
//filter:C
Asyoumighthaveguessedboth mapand filterarecalledfivetimesforeverystringin
theunderlyingcollectionwhereas forEachisonlycalledonce.
Wecangreatlyreducetheactualnumberofexecutionsifwechangetheorderofthe
operations,moving filtertothebeginningofthechain:
ModernJava-AGuidetoJava8
25Java8StreamTutorial

Stream.of("d2","a2","b1","b3","c")
.filter(s->{
System.out.println("filter:"+s);
returns.startsWith("a");
})
.map(s->{
System.out.println("map:"+s);
returns.toUpperCase();
})
.forEach(s->System.out.println("forEach:"+s));
//filter:d2
//filter:a2
//map:a2
//forEach:A2
//filter:b1
//filter:b3
//filter:c
Now, mapisonlycalledoncesotheoperationpipelineperformsmuchfasterforlarger
numbersofinputelements.Keepthatinmindwhencomposingcomplexmethodchains.
Let'sextendtheaboveexamplebyanadditionaloperation, sorted:
Stream.of("d2","a2","b1","b3","c")
.sorted((s1,s2)->{
System.out.printf("sort:%s;%s\n",s1,s2);
returns1.compareTo(s2);
})
.filter(s->{
System.out.println("filter:"+s);
returns.startsWith("a");
})
.map(s->{
System.out.println("map:"+s);
returns.toUpperCase();
})
.forEach(s->System.out.println("forEach:"+s));
Sortingisaspecialkindofintermediateoperation.It'sasocalledstatefuloperationsincein
ordertosortacollectionofelementsyouhavetomaintainstateduringordering.
Executingthisexampleresultsinthefollowingconsoleoutput:
sort:a2;d2
sort:b1;a2
sort:b1;d2
sort:b1;a2
sort:b3;b1
sort:b3;d2
sort:c;b3
sort:c;d2
filter:a2
map:a2
forEach:A2
filter:b1
filter:b3
filter:c
filter:d2
ModernJava-AGuidetoJava8
26Java8StreamTutorial

First,thesortoperationisexecutedontheentireinputcollection.Inotherwords sortedis
executedhorizontally.Sointhiscase sortediscalledeighttimesformultiplecombinations
oneveryelementintheinputcollection.
Onceagainwecanoptimizetheperformancebyreorderingthechain:
Stream.of("d2","a2","b1","b3","c")
.filter(s->{
System.out.println("filter:"+s);
returns.startsWith("a");
})
.sorted((s1,s2)->{
System.out.printf("sort:%s;%s\n",s1,s2);
returns1.compareTo(s2);
})
.map(s->{
System.out.println("map:"+s);
returns.toUpperCase();
})
.forEach(s->System.out.println("forEach:"+s));
//filter:d2
//filter:a2
//filter:b1
//filter:b3
//filter:c
//map:a2
//forEach:A2
Inthisexample sortedisneverbeencalledbecause filterreducestheinputcollection
tojustoneelement.Sotheperformanceisgreatlyincreasedforlargerinputcollections.
ReusingStreams
Java8streamscannotbereused.Assoonasyoucallanyterminaloperationthestreamis
closed:
Stream<String>stream=
Stream.of("d2","a2","b1","b3","c")
.filter(s->s.startsWith("a"));
stream.anyMatch(s->true);//ok
stream.noneMatch(s->true);//exception
Calling noneMatchafter anyMatchonthesamestreamresultsinthefollowingexception:
java.lang.IllegalStateException:streamhasalreadybeenoperateduponorclosed
atjava.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
atjava.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)
atcom.winterbe.java8.Streams5.test7(Streams5.java:38)
atcom.winterbe.java8.Streams5.main(Streams5.java:28)
ModernJava-AGuidetoJava8
27Java8StreamTutorial

Toovercomethislimitationwehavetotocreateanewstreamchainforeveryterminal
operationwewanttoexecute,e.g.wecouldcreateastreamsuppliertoconstructanew
streamwithallintermediateoperationsalreadysetup:
Supplier<Stream<String>>streamSupplier=
()->Stream.of("d2","a2","b1","b3","c")
.filter(s->s.startsWith("a"));
streamSupplier.get().anyMatch(s->true);//ok
streamSupplier.get().noneMatch(s->true);//ok
Eachcallto get()constructsanewstreamonwhichwearesavetocallthedesired
terminaloperation.
AdvancedOperations
Streamssupportplentyofdifferentoperations.We'vealreadylearnedaboutthemost
importantoperationslike filteror map.Ileaveituptoyoutodiscoverallotheravailable
operations(seeStreamJavadoc).Insteadlet'sdivedeeperintothemorecomplex
operations collect, flatMapand reduce.
Mostcodesamplesfromthissectionusethefollowinglistofpersonsfordemonstration
purposes:
classPerson{
Stringname;
intage;
Person(Stringname,intage){
this.name=name;
this.age=age;
}
@Override
publicStringtoString(){
returnname;
}
}
List<Person>persons=
Arrays.asList(
newPerson("Max",18),
newPerson("Peter",23),
newPerson("Pamela",23),
newPerson("David",12));
Collect
Collectisanextremelyusefulterminaloperationtotransformtheelementsofthestreaminto
adifferentkindofresult,e.g.a List, Setor Map.Collectacceptsa Collectorwhich
consistsoffourdifferentoperations:asupplier,anaccumulator,acombinerandafinisher.
ModernJava-AGuidetoJava8
28Java8StreamTutorial

Thissoundssupercomplicatedatfirst,butthegoodpartisJava8supportsvariousbuilt-in
collectorsviathe Collectorsclass.Soforthemostcommonoperationsyoudon'thaveto
implementacollectoryourself.
Let'sstartwithaverycommonusecase:
List<Person>filtered=
persons
.stream()
.filter(p->p.name.startsWith("P"))
.collect(Collectors.toList());
System.out.println(filtered);//[Peter,Pamela]
Asyoucanseeit'sverysimpletoconstructalistfromtheelementsofastream.Needaset
insteadoflist-justuse Collectors.toSet().
Thenextexamplegroupsallpersonsbyage:
Map<Integer,List<Person>>personsByAge=persons
.stream()
.collect(Collectors.groupingBy(p->p.age));
personsByAge
.forEach((age,p)->System.out.format("age%s:%s\n",age,p));
//age18:[Max]
//age23:[Peter,Pamela]
//age12:[David]
Collectorsareextremelyversatile.Youcanalsocreateaggregationsontheelementsofthe
stream,e.g.determiningtheaverageageofallpersons:
DoubleaverageAge=persons
.stream()
.collect(Collectors.averagingInt(p->p.age));
System.out.println(averageAge);//19.0
Ifyou'reinterestedinmorecomprehensivestatistics,thesummarizingcollectorsreturna
specialbuilt-insummarystatisticsobject.Sowecansimplydeterminemin,maxand
arithmeticaverageageofthepersonsaswellasthesumandcount.
IntSummaryStatisticsageSummary=
persons
.stream()
.collect(Collectors.summarizingInt(p->p.age));
System.out.println(ageSummary);
//IntSummaryStatistics{count=4,sum=76,min=12,average=19.000000,max=23}
Thenextexamplejoinsallpersonsintoasinglestring:
ModernJava-AGuidetoJava8
29Java8StreamTutorial

Stringphrase=persons
.stream()
.filter(p->p.age>=18)
.map(p->p.name)
.collect(Collectors.joining("and","InGermany","areoflegalage."));
System.out.println(phrase);
//InGermanyMaxandPeterandPamelaareoflegalage.
Thejoincollectoracceptsadelimiteraswellasanoptionalprefixandsuffix.
Inordertotransformthestreamelementsintoamap,wehavetospecifyhowboththekeys
andthevaluesshouldbemapped.Keepinmindthatthemappedkeysmustbeunique,
otherwisean IllegalStateExceptionisthrown.Youcanoptionallypassamergefunctionas
anadditionalparametertobypasstheexception:
Map<Integer,String>map=persons
.stream()
.collect(Collectors.toMap(
p->p.age,
p->p.name,
(name1,name2)->name1+";"+name2));
System.out.println(map);
//{18=Max,23=Peter;Pamela,12=David}
Nowthatweknowsomeofthemostpowerfulbuilt-incollectors,let'strytobuildourown
specialcollector.Wewanttotransformallpersonsofthestreamintoasinglestring
consistingofallnamesinupperlettersseparatedbythe |pipecharacter.Inorderto
achievethiswecreateanewcollectorvia Collector.of().Wehavetopassthefour
ingredientsofacollector:asupplier,anaccumulator,acombinerandafinisher.
Collector<Person,StringJoiner,String>personNameCollector=
Collector.of(
()->newStringJoiner("|"),//supplier
(j,p)->j.add(p.name.toUpperCase()),//accumulator
(j1,j2)->j1.merge(j2),//combiner
StringJoiner::toString);//finisher
Stringnames=persons
.stream()
.collect(personNameCollector);
System.out.println(names);//MAX|PETER|PAMELA|DAVID
SincestringsinJavaareimmutable,weneedahelperclasslike StringJoinertoletthe
collectorconstructourstring.ThesupplierinitiallyconstructssuchaStringJoinerwiththe
appropriatedelimiter.Theaccumulatorisusedtoaddeachpersonsupper-casednameto
theStringJoiner.ThecombinerknowshowtomergetwoStringJoinersintoone.Inthelast
stepthefinisherconstructsthedesiredStringfromtheStringJoiner.
FlatMap
ModernJava-AGuidetoJava8
30Java8StreamTutorial

We'vealreadylearnedhowtotransformtheobjectsofastreamintoanothertypeofobjects
byutilizingthe mapoperation.Mapiskindalimitedbecauseeveryobjectcanonlybe
mappedtoexactlyoneotherobject.Butwhatifwewanttotransformoneobjectintomultiple
othersornoneatall?Thisiswhere flatMapcomestotherescue.
FlatMaptransformseachelementofthestreamintoastreamofotherobjects.Soeach
objectwillbetransformedintozero,oneormultipleotherobjectsbackedbystreams.The
contentsofthosestreamswillthenbeplacedintothereturnedstreamofthe flatMap
operation.
Beforewesee flatMapinactionweneedanappropriatetypehierarchy:
classFoo{
Stringname;
List<Bar>bars=newArrayList<>();
Foo(Stringname){
this.name=name;
}
}
classBar{
Stringname;
Bar(Stringname){
this.name=name;
}
}
Next,weutilizeourknowledgeaboutstreamstoinstantiateacoupleofobjects:
List<Foo>foos=newArrayList<>();
//createfoos
IntStream
.range(1,4)
.forEach(i->foos.add(newFoo("Foo"+i)));
//createbars
foos.forEach(f->
IntStream
.range(1,4)
.forEach(i->f.bars.add(newBar("Bar"+i+"<-"+f.name))));
Nowwehavealistofthreefooseachconsistingofthreebars.
FlatMapacceptsafunctionwhichhastoreturnastreamofobjects.Soinordertoresolve
thebarobjectsofeachfoo,wejustpasstheappropriatefunction:
ModernJava-AGuidetoJava8
31Java8StreamTutorial

foos.stream()
.flatMap(f->f.bars.stream())
.forEach(b->System.out.println(b.name));
//Bar1<-Foo1
//Bar2<-Foo1
//Bar3<-Foo1
//Bar1<-Foo2
//Bar2<-Foo2
//Bar3<-Foo2
//Bar1<-Foo3
//Bar2<-Foo3
//Bar3<-Foo3
Asyoucansee,we'vesuccessfullytransformedthestreamofthreefooobjectsintoa
streamofninebarobjects.
Finally,theabovecodeexamplecanbesimplifiedintoasinglepipelineofstream
operations:
IntStream.range(1,4)
.mapToObj(i->newFoo("Foo"+i))
.peek(f->IntStream.range(1,4)
.mapToObj(i->newBar("Bar"+i+"<-"f.name))
.forEach(f.bars::add))
.flatMap(f->f.bars.stream())
.forEach(b->System.out.println(b.name));
FlatMapisalsoavailableforthe OptionalclassintroducedinJava8.Optionals flatMap
operationreturnsanoptionalobjectofanothertype.Soitcanbeutilizedtopreventnasty
nullchecks.
Thinkofahighlyhierarchicalstructurelikethis:
classOuter{
Nestednested;
}
classNested{
Innerinner;
}
classInner{
Stringfoo;
}
Inordertoresolvetheinnerstring fooofanouterinstanceyouhavetoaddmultiplenull
checkstopreventpossibleNullPointerExceptions:
Outerouter=newOuter();
if(outer!=null&&outer.nested!=null&&outer.nested.inner!=null){
System.out.println(outer.nested.inner.foo);
}
Thesamebehaviorcanbeobtainedbyutilizingoptionals flatMapoperation:
ModernJava-AGuidetoJava8
32Java8StreamTutorial

Optional.of(newOuter())
.flatMap(o->Optional.ofNullable(o.nested))
.flatMap(n->Optional.ofNullable(n.inner))
.flatMap(i->Optional.ofNullable(i.foo))
.ifPresent(System.out::println);
Eachcallto flatMapreturnsan Optionalwrappingthedesiredobjectifpresentor nullif
absent.
Reduce
Thereductionoperationcombinesallelementsofthestreamintoasingleresult.Java8
supportsthreedifferentkindof reducemethods.Thefirstonereducesastreamof
elementstoexactlyoneelementofthestream.Let'sseehowwecanusethismethodto
determinetheoldestperson:
persons
.stream()
.reduce((p1,p2)->p1.age>p2.age?p1:p2)
.ifPresent(System.out::println);//Pamela
The reducemethodacceptsa BinaryOperatoraccumulatorfunction.That'sactuallya
BiFunctionwherebothoperandssharethesametype,inthatcase Person.BiFunctions
arelike Functionbutaccepttwoarguments.Theexamplefunctioncomparesbothpersons
agesinordertoreturnthepersonwiththemaximumage.
Thesecond reducemethodacceptsbothanidentityvalueanda BinaryOperator
accumulator.ThismethodcanbeutilizedtoconstructanewPersonwiththeaggregated
namesandagesfromallotherpersonsinthestream:
Personresult=
persons
.stream()
.reduce(newPerson("",0),(p1,p2)->{
p1.age+=p2.age;
p1.name+=p2.name;
returnp1;
});
System.out.format("name=%s;age=%s",result.name,result.age);
//name=MaxPeterPamelaDavid;age=76
Thethird reducemethodacceptsthreeparameters:anidentityvalue,a BiFunction
accumulatorandacombinerfunctionoftype BinaryOperator.Sincetheidentityvaluestype
isnotrestrictedtothe Persontype,wecanutilizethisreductiontodeterminethesumof
agesfromallpersons:
ModernJava-AGuidetoJava8
33Java8StreamTutorial

IntegerageSum=persons
.stream()
.reduce(0,(sum,p)->sum+=p.age,(sum1,sum2)->sum1+sum2);
System.out.println(ageSum);//76
Asyoucanseetheresultis76,butwhat'shappeningexactlyunderthehood?Let'sextend
theabovecodebysomedebugoutput:
IntegerageSum=persons
.stream()
.reduce(0,
(sum,p)->{
System.out.format("accumulator:sum=%s;person=%s\n",sum,p);
returnsum+=p.age;
},
(sum1,sum2)->{
System.out.format("combiner:sum1=%s;sum2=%s\n",sum1,sum2);
returnsum1+sum2;
});
//accumulator:sum=0;person=Max
//accumulator:sum=18;person=Peter
//accumulator:sum=41;person=Pamela
//accumulator:sum=64;person=David
Asyoucanseetheaccumulatorfunctiondoesallthework.Itfirstgetcalledwiththeinitial
identityvalue0andthefirstpersonMax.Inthenextthreesteps sumcontinuallyincreases
bytheageofthelaststepspersonuptoatotalageof76.
Waitwat?Thecombinernevergetscalled?Executingthesamestreaminparallelwillliftthe
secret:
IntegerageSum=persons
.parallelStream()
.reduce(0,
(sum,p)->{
System.out.format("accumulator:sum=%s;person=%s\n",sum,p);
returnsum+=p.age;
},
(sum1,sum2)->{
System.out.format("combiner:sum1=%s;sum2=%s\n",sum1,sum2);
returnsum1+sum2;
});
//accumulator:sum=0;person=Pamela
//accumulator:sum=0;person=David
//accumulator:sum=0;person=Max
//accumulator:sum=0;person=Peter
//combiner:sum1=18;sum2=23
//combiner:sum1=23;sum2=12
//combiner:sum1=41;sum2=35
Executingthisstreaminparallelresultsinanentirelydifferentexecutionbehavior.Nowthe
combinerisactuallycalled.Sincetheaccumulatoriscalledinparallel,thecombineris
neededtosumuptheseparateaccumulatedvalues.
ModernJava-AGuidetoJava8
34Java8StreamTutorial

Let'sdivedeeperintoparallelstreamsinthenextchapter.
ParallelStreams
Streamscanbeexecutedinparalleltoincreaseruntimeperformanceonlargeamountof
inputelements.Parallelstreamsuseacommon ForkJoinPoolavailableviathestatic
ForkJoinPool.commonPool()method.Thesizeoftheunderlyingthread-poolusesuptofive
threads-dependingontheamountofavailablephysicalCPUcores:
ForkJoinPoolcommonPool=ForkJoinPool.commonPool();
System.out.println(commonPool.getParallelism());//3
Onmymachinethecommonpoolisinitializedwithaparallelismof3perdefault.Thisvalue
canbedecreasedorincreasedbysettingthefollowingJVMparameter:
-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
Collectionssupportthemethod parallelStream()tocreateaparallelstreamofelements.
Alternativelyyoucancalltheintermediatemethod parallel()onagivenstreamtoconvert
asequentialstreamtoaparallelcounterpart.
Inordertounderstatetheparallelexecutionbehaviorofaparallelstreamthenextexample
printsinformationaboutthecurrentthreadto sout:
Arrays.asList("a1","a2","b1","c2","c1")
.parallelStream()
.filter(s->{
System.out.format("filter:%s[%s]\n",
s,Thread.currentThread().getName());
returntrue;
})
.map(s->{
System.out.format("map:%s[%s]\n",
s,Thread.currentThread().getName());
returns.toUpperCase();
})
.forEach(s->System.out.format("forEach:%s[%s]\n",
s,Thread.currentThread().getName()));
Byinvestigatingthedebugoutputweshouldgetabetterunderstandingwhichthreadsare
actuallyusedtoexecutethestreamoperations:
ModernJava-AGuidetoJava8
35Java8StreamTutorial

filter:b1[main]
filter:a2[ForkJoinPool.commonPool-worker-1]
map:a2[ForkJoinPool.commonPool-worker-1]
filter:c2[ForkJoinPool.commonPool-worker-3]
map:c2[ForkJoinPool.commonPool-worker-3]
filter:c1[ForkJoinPool.commonPool-worker-2]
map:c1[ForkJoinPool.commonPool-worker-2]
forEach:C2[ForkJoinPool.commonPool-worker-3]
forEach:A2[ForkJoinPool.commonPool-worker-1]
map:b1[main]
forEach:B1[main]
filter:a1[ForkJoinPool.commonPool-worker-3]
map:a1[ForkJoinPool.commonPool-worker-3]
forEach:A1[ForkJoinPool.commonPool-worker-3]
forEach:C1[ForkJoinPool.commonPool-worker-2]
Asyoucanseetheparallelstreamutilizesallavailablethreadsfromthecommon
ForkJoinPoolforexecutingthestreamoperations.Theoutputmaydifferinconsecutive
runsbecausethebehaviorwhichparticularthreadisactuallyusedisnon-deterministic.
Let'sextendtheexamplebyanadditionalstreamoperation, sort:
Arrays.asList("a1","a2","b1","c2","c1")
.parallelStream()
.filter(s->{
System.out.format("filter:%s[%s]\n",
s,Thread.currentThread().getName());
returntrue;
})
.map(s->{
System.out.format("map:%s[%s]\n",
s,Thread.currentThread().getName());
returns.toUpperCase();
})
.sorted((s1,s2)->{
System.out.format("sort:%s<>%s[%s]\n",
s1,s2,Thread.currentThread().getName());
returns1.compareTo(s2);
})
.forEach(s->System.out.format("forEach:%s[%s]\n",
s,Thread.currentThread().getName()));
Theresultmaylookstrangeatfirst:
ModernJava-AGuidetoJava8
36Java8StreamTutorial

filter:c2[ForkJoinPool.commonPool-worker-3]
filter:c1[ForkJoinPool.commonPool-worker-2]
map:c1[ForkJoinPool.commonPool-worker-2]
filter:a2[ForkJoinPool.commonPool-worker-1]
map:a2[ForkJoinPool.commonPool-worker-1]
filter:b1[main]
map:b1[main]
filter:a1[ForkJoinPool.commonPool-worker-2]
map:a1[ForkJoinPool.commonPool-worker-2]
map:c2[ForkJoinPool.commonPool-worker-3]
sort:A2<>A1[main]
sort:B1<>A2[main]
sort:C2<>B1[main]
sort:C1<>C2[main]
sort:C1<>B1[main]
sort:C1<>C2[main]
forEach:A1[ForkJoinPool.commonPool-worker-1]
forEach:C2[ForkJoinPool.commonPool-worker-3]
forEach:B1[main]
forEach:A2[ForkJoinPool.commonPool-worker-2]
forEach:C1[ForkJoinPool.commonPool-worker-1]
Itseemsthat sortisexecutedsequentiallyonthemainthreadonly.Actually, sortona
parallelstreamusesthenewJava8method Arrays.parallelSort()underthehood.As
statedinJavadocthismethoddecidesonthelengthofthearrayifsortingwillbeperformed
sequentiallyorinparallel:
>Ifthelengthofthespecifiedarrayislessthantheminimumgranularity,thenitissorted
usingtheappropriateArrays.sortmethod.
Comingbacktothe reduceexamplefromthelastsection.Wealreadyfoundoutthatthe
combinerfunctionisonlycalledinparallelbutnotinsequentialstreams.Let'sseewhich
threadsareactuallyinvolved:
List<Person>persons=Arrays.asList(
newPerson("Max",18),
newPerson("Peter",23),
newPerson("Pamela",23),
newPerson("David",12));
persons
.parallelStream()
.reduce(0,
(sum,p)->{
System.out.format("accumulator:sum=%s;person=%s[%s]\n",
sum,p,Thread.currentThread().getName());
returnsum+=p.age;
},
(sum1,sum2)->{
System.out.format("combiner:sum1=%s;sum2=%s[%s]\n",
sum1,sum2,Thread.currentThread().getName());
returnsum1+sum2;
});
Theconsoleoutputrevealsthatboththeaccumulatorandthecombinerfunctionsare
executedinparallelonallavailablethreads:
ModernJava-AGuidetoJava8
37Java8StreamTutorial

accumulator:sum=0;person=Pamela;[main]
accumulator:sum=0;person=Max;[ForkJoinPool.commonPool-worker-3]
accumulator:sum=0;person=David;[ForkJoinPool.commonPool-worker-2]
accumulator:sum=0;person=Peter;[ForkJoinPool.commonPool-worker-1]
combiner:sum1=18;sum2=23;[ForkJoinPool.commonPool-worker-1]
combiner:sum1=23;sum2=12;[ForkJoinPool.commonPool-worker-2]
combiner:sum1=41;sum2=35;[ForkJoinPool.commonPool-worker-2]
Insummary,itcanbestatedthatparallelstreamscanbringbeaniceperformanceboostto
streamswithalargeamountofinputelements.Butkeepinmindthatsomeparallelstream
operationslike reduceand collectneedadditionalcomputations(combineoperations)
whichisn'tneededwhenexecutedsequentially.
Furthermorewe'velearnedthatallparallelstreamoperationssharethesameJVM-wide
common ForkJoinPool.Soyouprobablywanttoavoidimplementingslowblockingstream
operationssincethatcouldpotentiallyslowdownotherpartsofyourapplicationwhichrely
heavilyonparallelstreams.
That'sit
MyprogrammingguidetoJava8streamsendshere.Ifyou'reinterestedinlearningmore
aboutJava8streams,IrecommendtoyoutheStreamJavadocpackagedocumentation.If
youwanttolearnmoreabouttheunderlyingmechanisms,youprobablywanttoreadMartin
FowlersarticleaboutCollectionPipelines.
Ifyou'reinterestedinJavaScriptaswell,youmaywanttohavealookatStream.js-a
JavaScriptimplementationoftheJava8StreamsAPI.YoumayalsowannareadmyJava8
TutorialandmyJava8NashornTutorial.
Hopefullythistutorialwashelpfultoyouandyou'veenjoyedreadingit.Thefullsourcecode
ofthetutorialsamplesishostedonGitHub.Feelfreetoforktherepositoryorsendmeyour
feedbackviaTwitter.
Happycoding!
ModernJava-AGuidetoJava8
38Java8StreamTutorial

Java8NashornTutorial
April05,2014
LearnallabouttheNashornJavascriptEnginewitheasilyunderstoodcodeexamples.The
NashornJavascriptEngineispartofJavaSE8andcompeteswithotherstandalone
engineslikeGoogleV8(theenginethatpowersGoogleChromeandNode.js).Nashorn
extendsJavascapabilitiesbyrunningdynamicjavascriptcodenativelyontheJVM.
Inthenext~15minutesyoulearnhowtoevaluatejavascriptontheJVMdynamicallyduring
runtime.ThemostrecentNashornlanguagefeaturesaredemonstratedwithsmallcode
examples.Youlearnhowtocalljavascriptfunctionsfromjavacodeandviceversa.Atthe
endyou'rereadytointegratedynamicscriptsinyourdailyjavabusiness.
UPDATE-I'mcurrentlyworkingonaJavaScriptimplementationoftheJava8StreamsAPI
forthebrowser.IfI'vedrawnyourinterestcheckoutStream.jsonGitHub.YourFeedbackis
highlyappreciated.
UsingNashorn
TheNashornjavascriptenginecaneitherbeusedprogrammaticallyfromjavaprogramsor
byutilizingthecommandlinetool jjs,whichislocatedin $JAVA_HOME/bin.Ifyouplanto
workwith jjsyoumightwanttoputasymboliclinkforsimpleaccess:
ModernJava-AGuidetoJava8
39Java8NashornTutorial

$cd/usr/bin
$ln-s$JAVA_HOME/bin/jjsjjs
$jjs
jjs>print('HelloWorld');
Thistutorialfocusesonusingnashornfromjavacode,solet'sskip jjsfornow.Asimple
HelloWorldinjavacodelookslikethis:
ScriptEngineengine=newScriptEngineManager().getEngineByName("nashorn");
engine.eval("print('HelloWorld!');");
Inordertoevaluatejavascriptcodefromjava,youfirstcreateanashornscriptengineby
utilizingthe javax.scriptpackagealreadyknownfromRhino(Javaslegacyjsenginefrom
Mozilla).
Javascriptcodecaneitherbeevaluateddirectlybypassingjavascriptcodeasastringas
shownabove.Oryoucanpassafilereaderpointingtoyour.jsscriptfile:
ScriptEngineengine=newScriptEngineManager().getEngineByName("nashorn");
engine.eval(newFileReader("script.js"));
NashornjavascriptisbasedonECMAScript5.1butfutureversionsofnashornwillinclude
supportforECMAScript6:
>ThecurrentstrategyforNashornistofollowtheECMAScriptspecification.Whenwe
releasewithJDK8wewillbealignedwithECMAScript5.1.Thefollowupmajorreleaseof
NashornwillalignwithECMAScriptEdition6.
NashorndefinesalotoflanguageandAPIextensionstotheECMAScriptstandard.Butfirst
let'stakealookathowthecommunicationbetweenjavaandjavascriptcodeworks.
InvokingJavascriptFunctionsfromJava
Nashornsupportstheinvocationofjavascriptfunctionsdefinedinyourscriptfilesdirectly
fromjavacode.Youcanpassjavaobjectsasfunctionargumentsandreturndatabackfrom
thefunctiontothecallingjavamethod.
Thefollowingjavascriptfunctionswilllaterbecalledfromthejavaside:
varfun1=function(name){
print('HitherefromJavascript,'+name);
return"greetingsfromjavascript";
};
varfun2=function(object){
print("JSClassDefinition:"+Object.prototype.toString.call(object));
};
ModernJava-AGuidetoJava8
40Java8NashornTutorial

Inordertocallafunctionyoufirsthavetocastthescriptengineto Invocable.The
Invocableinterfaceisimplementedbythe NashornScriptEngineimplementationanddefines
amethod invokeFunctiontocallajavascriptfunctionforagivenname.
ScriptEngineengine=newScriptEngineManager().getEngineByName("nashorn");
engine.eval(newFileReader("script.js"));
Invocableinvocable=(Invocable)engine;
Objectresult=invocable.invokeFunction("fun1","PeterParker");
System.out.println(result);
System.out.println(result.getClass());
//HitherefromJavascript,PeterParker
//greetingsfromjavascript
//classjava.lang.String
Executingthecoderesultsinthreelineswrittentotheconsole.Callingthefunction print
pipestheresultto System.out,soweseethejavascriptmessagefirst.
Nowlet'scallthesecondfunctionbypassingarbitraryjavaobjects:
invocable.invokeFunction("fun2",newDate());
//[objectjava.util.Date]
invocable.invokeFunction("fun2",LocalDateTime.now());
//[objectjava.time.LocalDateTime]
invocable.invokeFunction("fun2",newPerson());
//[objectcom.winterbe.java8.Person]
Javaobjectscanbepassedwithoutloosinganytypeinformationonthejavascriptside.
SincethescriptrunsnativelyontheJVMwecanutilizethefullpoweroftheJavaAPIor
externallibrariesonnashorn.
InvokingJavaMethodsfromJavascript
Invokingjavamethodsfromjavascriptisquiteeasy.Wefirstdefineastaticjavamethod:
staticStringfun1(Stringname){
System.out.format("HitherefromJava,%s",name);
return"greetingsfromjava";
}
Javaclassescanbereferencedfromjavascriptviathe Java.typeAPIextension.It'ssimilar
toimportingclassesinjavacode.Assoonasthejavatypeisdefinedwenaturallycallthe
staticmethod fun1()andprinttheresultto sout.Sincethemethodisstatic,wedon't
havetocreateaninstancefirst.
ModernJava-AGuidetoJava8
41Java8NashornTutorial

varMyJavaClass=Java.type('my.package.MyJavaClass');
varresult=MyJavaClass.fun1('JohnDoe');
print(result);
//HitherefromJava,JohnDoe
//greetingsfromjava
HowdoesNashornhandletypeconversionwhencallingjavamethodswithnativejavascript
types?Let'sfindoutwithasimpleexample.
Thefollowingjavamethodsimplyprintstheactualclasstypeofthemethodparameter:
staticvoidfun2(Objectobject){
System.out.println(object.getClass());
}
Tounderstandhowtypeconversationsarehandledunderthehood,wecallthismethodwith
differentjavascripttypes:
MyJavaClass.fun2(123);
//classjava.lang.Integer
MyJavaClass.fun2(49.99);
//classjava.lang.Double
MyJavaClass.fun2(true);
//classjava.lang.Boolean
MyJavaClass.fun2("hithere")
//classjava.lang.String
MyJavaClass.fun2(newNumber(23));
//classjdk.nashorn.internal.objects.NativeNumber
MyJavaClass.fun2(newDate());
//classjdk.nashorn.internal.objects.NativeDate
MyJavaClass.fun2(newRegExp());
//classjdk.nashorn.internal.objects.NativeRegExp
MyJavaClass.fun2({foo:'bar'});
//classjdk.nashorn.internal.scripts.JO4
Primitivejavascripttypesareconvertedtotheappropriatejavawrapperclass.Insteadnative
javascriptobjectsarerepresentedbyinternaladapterclasses.Pleasekeepinmindthat
classesfrom jdk.nashorn.internalaresubjecttochange,soyoushouldn'tprogramagainst
thoseclassesinclient-code:
>Anythingmarkedinternalwilllikelychangeoutfromunderneathyou.
ScriptObjectMirror
ModernJava-AGuidetoJava8
42Java8NashornTutorial

Whenpassingnativejavascriptobjectstojavayoucanutilizetheclass ScriptObjectMirror
whichisactuallyajavarepresentationoftheunderlyingjavascriptobject.ScriptObjectMirror
implementsthemapinterfaceandresidesinsidethepackage jdk.nashorn.api.Classes
fromthispackageareintendedtobeusedinclient-code.
Thenextsamplechangestheparametertypefrom Objectto ScriptObjectMirrorsowe
canextractsomeinfosfromthepassedjavascriptobject:
staticvoidfun3(ScriptObjectMirrormirror){
System.out.println(mirror.getClassName()+":"+
Arrays.toString(mirror.getOwnKeys(true)));
}
Whenpassinganobjecthashtothismethod,thepropertiesareaccessibleonthejavaside:
MyJavaClass.fun3({
foo:'bar',
bar:'foo'
});
//Object:[foo,bar]
Wecanalsocallmemberfunctionsonjavascriptobjectfromjava.Let'sfirstdefinea
javascripttypePersonwithproperties firstNameand lastNameandmethod getFullName.
functionPerson(firstName,lastName){
this.firstName=firstName;
this.lastName=lastName;
this.getFullName=function(){
returnthis.firstName+""+this.lastName;
}
}
Thejavascriptmethod getFullNamecanbecalledontheScriptObjectMirrorvia
callMember().
staticvoidfun4(ScriptObjectMirrorperson){
System.out.println("FullNameis:"+person.callMember("getFullName"));
}
Whenpassinganewpersontothejavamethod,weseethedesiredresultontheconsole:
varperson1=newPerson("Peter","Parker");
MyJavaClass.fun4(person1);
//FullNameis:PeterParker
LanguageExtensions
ModernJava-AGuidetoJava8
43Java8NashornTutorial

NashorndefinesvariouslanguageandAPIextensionstotheECMAScriptstandard.Let's
headrightintothemostrecentfeatures:
TypedArrays
Nativejavascriptarraysareuntyped.Nashornenablesyoutousetypedjavaarraysin
javascript:
varIntArray=Java.type("int[]");
vararray=newIntArray(5);
array[0]=5;
array[1]=4;
array[2]=3;
array[3]=2;
array[4]=1;
try{
array[5]=23;
}catch(e){
print(e.message);//Arrayindexoutofrange:5
}
array[0]="17";
print(array[0]);//17
array[0]="wrongtype";
print(array[0]);//0
array[0]="17.3";
print(array[0]);//17
The int[]arraybehaveslikearealjavaintarray.ButadditionallyNashornperforms
implicittypeconversionsunderthehoodwhenwe'retryingtoaddnon-integervaluestothe
array.Stringswillbeauto-convertedtointwhichisquitehandy.
CollectionsandForEach
Insteadofmessingaroundwitharrayswecanuseanyjavacollection.Firstdefinethejava
typevia Java.type,thencreatenewinstancesondemand.
varArrayList=Java.type('java.util.ArrayList');
varlist=newArrayList();
list.add('a');
list.add('b');
list.add('c');
foreach(varelinlist)print(el);//a,b,c
InordertoiterateovercollectionsandarraysNashornintroducesthe foreachstatement.It
worksjustliketheforeachloopinjava.
Here'sanothercollectionforeachexample,utilizing HashMap:
ModernJava-AGuidetoJava8
44Java8NashornTutorial

varmap=newjava.util.HashMap();
map.put('foo','val1');
map.put('bar','val2');
foreach(vareinmap.keySet())print(e);//foo,bar
foreach(vareinmap.values())print(e);//val1,val2
LambdaexpressionsandStreams
Everyoneloveslambdasandstreams-sodoesNashorn!AlthoughECMAScript5.1lacks
thecompactarrowsyntaxfromtheJava8lambdaexpressions,wecanusefunctionliterals
whereeverlambdaexpressionsareaccepted.
varlist2=newjava.util.ArrayList();
list2.add("ddd2");
list2.add("aaa2");
list2.add("bbb1");
list2.add("aaa1");
list2.add("bbb3");
list2.add("ccc");
list2.add("bbb2");
list2.add("ddd1");
list2
.stream()
.filter(function(el){
returnel.startsWith("aaa");
})
.sorted()
.forEach(function(el){
print(el);
});
//aaa1,aaa2
Extendingclasses
Javatypescansimplybeextendedwiththe Java.extendextension.Asyoucanseeinthe
nextexample,youcanevencreatemulti-threadedcodeinyourscripts:
varRunnable=Java.type('java.lang.Runnable');
varPrinter=Java.extend(Runnable,{
run:function(){
print('printedfromaseparatethread');
}
});
varThread=Java.type('java.lang.Thread');
newThread(newPrinter()).start();
newThread(function(){
print('printedfromanotherthread');
}).start();
//printedfromaseparatethread
//printedfromanotherthread
Parameteroverloading
ModernJava-AGuidetoJava8
45Java8NashornTutorial

Methodsandfunctionscaneitherbecalledwiththepointnotationorwiththesquarebraces
notation.
varSystem=Java.type('java.lang.System');
System.out.println(10);//10
System.out["println"](11.0);//11.0
System.out["println(double)"](12);//12.0
Passingtheoptionalparametertype println(double)whencallingamethodwith
overloadedparametersdeterminestheexactmethodtobecalled.
JavaBeans
Insteadofexplicitlyworkingwithgettersandsettersyoucanjustusesimplepropertynames
bothforgettingorsettingvaluesfromajavabean.
varDate=Java.type('java.util.Date');
vardate=newDate();
date.year+=1900;
print(date.year);//2014
FunctionLiterals
Forsimpleonelinefunctionswecanskipthecurlybraces:
functionsqr(x)x*x;
print(sqr(3));//9
Bindingproperties
Propertiesfromtwodifferentobjectscanbeboundtogether:
varo1={};
varo2={foo:'bar'};
Object.bindProperties(o1,o2);
print(o1.foo);//bar
o1.foo='BAM';
print(o2.foo);//BAM
Trimmingstrings
Ilikemystringstrimmed.
print("hehe".trimLeft());//hehe
print("hehe".trimRight()+"he");//hehehe
Whereis
ModernJava-AGuidetoJava8
46Java8NashornTutorial

Incaseyouforgetwhereyouare:
print(__FILE__,__LINE__,__DIR__);
ImportScopes
Sometimesit'susefultoimportmanyjavapackagesatonce.Wecanusetheclass
JavaImportertobeusedinconjunctionwiththe withstatement.Allclassfilesfromthe
importedpackagesareaccessiblewithinthelocalscopeofthe withstatement:
varimports=newJavaImporter(java.io,java.lang);
with(imports){
varfile=newFile(__FILE__);
System.out.println(file.getAbsolutePath());
///path/to/my/script.js
}
Convertarrays
Somepackageslike java.utilcanbeaccesseddirectlywithoututilizing Java.typeor
JavaImporter:
varlist=newjava.util.ArrayList();
list.add("s1");
list.add("s2");
list.add("s3");
Thiscodeconvertsthejavalisttoanativejavascriptarray:
varjsArray=Java.from(list);
print(jsArray);//s1,s2,s3
print(Object.prototype.toString.call(jsArray));//[objectArray]
Andtheotherwayaround:
varjavaArray=Java.to([3,5,7,11],"int[]");
CallingSuper
Accessingoverriddenmembersinjavascriptistraditionallyawkwardbecausejavas super
keyworddoesn'texistinECMAScript.Luckilynashorngoestotherescue.
Firstwedefineasupertypeinjavacode:
ModernJava-AGuidetoJava8
47Java8NashornTutorial

classSuperRunnerimplementsRunnable{
@Override
publicvoidrun(){
System.out.println("superrun");
}
}
Nextweoverride SuperRunnerfromjavascript.Payattentiontotheextendednashorn
syntaxwhencreatinganew Runnerinstance:Thesyntaxofoverridingmembersis
borrowedfromjavasanonymousobjects.
varSuperRunner=Java.type('com.winterbe.java8.SuperRunner');
varRunner=Java.extend(SuperRunner);
varrunner=newRunner(){
run:function(){
Java.super(runner).run();
print('onmyrun');
}
}
runner.run();
//superrun
//onmyrun
Wecalltheoverriddenmethod SuperRunner.run()byutilizingthe Java.superextension.
Loadingscripts
Evaluatingadditionalscriptfilesfromjavascriptisquiteeasy.Wecanloadbothlocalor
remotescriptswiththe loadfunction.
I'musingUnderscore.jsalotformywebfront-ends,solet'sreuseUnderscoreinNashorn:
load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js');
varodds=_.filter([1,2,3,4,5,6],function(num){
returnnum%2==1;
});
print(odds);//1,3,5
Theexternalscriptwillbeevaluatedinthesamejavascriptcontext,sowecanaccessthe
underscorevariabledirectly.Keepinmindthatloadingscriptscanpotentiallybreakyourown
codewhenvariablenamesareoverlappingeachother.
Thisproblemcanbebypassedbyloadingscriptfilesintoanewglobalcontext:
loadWithNewGlobal('script.js');
Command-linescripts
ModernJava-AGuidetoJava8
48Java8NashornTutorial

Ifyou'reinterestedinwritingcommand-line(shell)scriptswithJava,giveNakeatry.Nakeis
asimplifiedMakeforJava8Nashorn.Youdefinetasksinaproject-specific Nakefile,then
runthosetasksbytyping nake--myTaskintothecommandline.Tasksarewrittenin
javascriptandruninNashornsscriptingmode,soyoucanutilizethefullpowerofyour
terminalaswellastheJDK8APIandanyjavalibrary.
ForJavaDeveloperswritingcommand-linescriptsiseasyasneverbefore...
That'sit
IhopethisguidewashelpfultoyouandyouenjoyedourjourneytotheNashornJavascript
Engine.ForfurtherinformationaboutNashornreadhere,hereandhere.Aguidetocoding
shellscriptswithNashorncanbefoundhere.
IrecentlypublishedafollowuparticleabouthowtouseBackbone.jsmodelswiththe
NashornJavascriptEngine.IfyouwanttolearnmoreaboutJava8feelfreetoreadmyJava
8TutorialandmyJava8StreamTutorial.
TherunnablesourcecodefromthisNashorntutorialishostedonGitHub.Feelfreetofork
therepositoryorsendmeyourfeedbackviaTwitter.
Keeponcoding!
ModernJava-AGuidetoJava8
49Java8NashornTutorial

Java8ConcurrencyTutorial:Threadsand
Executors
April07,2015
WelcometothefirstpartofmyJava8Concurrencytutorial.Thisguideteachesyou
concurrentprogramminginJava8witheasilyunderstoodcodeexamples.It'sthefirstpart
outofaseriesoftutorialscoveringtheJavaConcurrencyAPI.Inthenext15minyoulearn
howtoexecutecodeinparallelviathreads,tasksandexecutorservices.
Part1:ThreadsandExecutors
Part2:SynchronizationandLocks
Part3:AtomicVariablesandConcurrentMap
TheConcurrencyAPIwasfirstintroducedwiththereleaseofJava5andthenprogressively
enhancedwitheverynewJavarelease.Themajorityofconceptsshowninthisarticlealso
workinolderversionsofJava.HowevermycodesamplesfocusonJava8andmakeheavy
useoflambdaexpressionsandothernewfeatures.Ifyou'renotyetfamiliarwithlambdasI
recommendreadingmyJava8Tutorialfirst.
ThreadsandRunnables
Allmodernoperatingsystemssupportconcurrencybothviaprocesses)andthreads.
Processesareinstancesofprogramswhichtypicallyrunindependenttoeachother,e.g.if
youstartajavaprogramtheoperatingsystemspawnsanewprocesswhichrunsinparallel
tootherprograms.Insidethoseprocesseswecanutilizethreadstoexecutecode
concurrently,sowecanmakethemostoutoftheavailablecoresoftheCPU.
JavasupportsThreadssinceJDK1.0.Beforestartinganewthreadyouhavetospecifythe
codetobeexecutedbythisthread,oftencalledthetask.Thisisdonebyimplementing
Runnable-afunctionalinterfacedefiningasinglevoidno-argsmethod run()as
demonstratedinthefollowingexample:
Runnabletask=()->{
StringthreadName=Thread.currentThread().getName();
System.out.println("Hello"+threadName);
};
task.run();
Threadthread=newThread(task);
thread.start();
System.out.println("Done!");
ModernJava-AGuidetoJava8
50Java8ConcurrencyTutorial:ThreadsandExecutors

Since RunnableisafunctionalinterfacewecanutilizeJava8lambdaexpressionstoprint
thecurrentthreadsnametotheconsole.Firstweexecutetherunnabledirectlyonthemain
threadbeforestartinganewthread.
Theresultontheconsolemightlooklikethis:
Hellomain
HelloThread-0
Done!
Orthat:
Hellomain
Done!
HelloThread-0
Duetoconcurrentexecutionwecannotpredictiftherunnablewillbeinvokedbeforeorafter
printing'done'.Theorderisnon-deterministic,thusmakingconcurrentprogramminga
complextaskinlargerapplications.
Threadscanbeputtosleepforacertainduration.Thisisquitehandytosimulatelong
runningtasksinthesubsequentcodesamplesofthisarticle:
Runnablerunnable=()->{
try{
Stringname=Thread.currentThread().getName();
System.out.println("Foo"+name);
TimeUnit.SECONDS.sleep(1);
System.out.println("Bar"+name);
}
catch(InterruptedExceptione){
e.printStackTrace();
}
};
Threadthread=newThread(runnable);
thread.start();
Whenyouruntheabovecodeyou'llnoticetheoneseconddelaybetweenthefirstandthe
secondprintstatement. TimeUnitisausefulenumforworkingwithunitsoftime.
Alternativelyyoucanachievethesamebycalling Thread.sleep(1000).
Workingwiththe Threadclasscanbeverytediousanderror-prone.Duetothatreasonthe
ConcurrencyAPIhasbeenintroducedbackin2004withthereleaseofJava5.TheAPIis
locatedinpackage java.util.concurrentandcontainsmanyusefulclassesforhandling
concurrentprogramming.SincethattimetheConcurrencyAPIhasbeenenhancedwith
everynewJavareleaseandevenJava8providesnewclassesandmethodsfordealingwith
concurrency.
ModernJava-AGuidetoJava8
51Java8ConcurrencyTutorial:ThreadsandExecutors

Nowlet'stakeadeeperlookatoneofthemostimportantpartsoftheConcurrencyAPI-the
executorservices.
Executors
TheConcurrencyAPIintroducestheconceptofan ExecutorServiceasahigherlevel
replacementforworkingwiththreadsdirectly.Executorsarecapableofrunning
asynchronoustasksandtypicallymanageapoolofthreads,sowedon'thavetocreatenew
threadsmanually.Allthreadsoftheinternalpoolwillbereusedunderthehoodforrevenant
tasks,sowecanrunasmanyconcurrenttasksaswewantthroughoutthelife-cycleofour
applicationwithasingleexecutorservice.
Thisishowthefirstthread-examplelookslikeusingexecutors:
ExecutorServiceexecutor=Executors.newSingleThreadExecutor();
executor.submit(()->{
StringthreadName=Thread.currentThread().getName();
System.out.println("Hello"+threadName);
});
//=>Hellopool-1-thread-1
Theclass Executorsprovidesconvenientfactorymethodsforcreatingdifferentkindsof
executorservices.Inthissampleweuseanexecutorwithathreadpoolofsizeone.
Theresultlookssimilartotheabovesamplebutwhenrunningthecodeyou'llnoticean
importantdifference:thejavaprocessneverstops!Executorshavetobestoppedexplicitly-
otherwisetheykeeplisteningfornewtasks.
An ExecutorServiceprovidestwomethodsforthatpurpose: shutdown()waitsforcurrently
runningtaskstofinishwhile shutdownNow()interruptsallrunningtasksandshutthe
executordownimmediately.
ThisisthepreferredwayhowItypicallyshutdownexecutors:
try{
System.out.println("attempttoshutdownexecutor");
executor.shutdown();
executor.awaitTermination(5,TimeUnit.SECONDS);
}
catch(InterruptedExceptione){
System.err.println("tasksinterrupted");
}
finally{
if(!executor.isTerminated()){
System.err.println("cancelnon-finishedtasks");
}
executor.shutdownNow();
System.out.println("shutdownfinished");
}
ModernJava-AGuidetoJava8
52Java8ConcurrencyTutorial:ThreadsandExecutors

Theexecutorshutsdownsoftlybywaitingacertainamountoftimeforterminationof
currentlyrunningtasks.Afteramaximumoffivesecondstheexecutorfinallyshutsdownby
interruptingallrunningtasks.
CallablesandFutures
Inadditionto Runnableexecutorssupportanotherkindoftasknamed Callable.Callables
arefunctionalinterfacesjustlikerunnablesbutinsteadofbeing voidtheyreturnavalue.
Thislambdaexpressiondefinesacallablereturninganintegeraftersleepingforonesecond:
Callable<Integer>task=()->{
try{
TimeUnit.SECONDS.sleep(1);
return123;
}
catch(InterruptedExceptione){
thrownewIllegalStateException("taskinterrupted",e);
}
};
Callablescanbesubmittedtoexecutorservicesjustlikerunnables.Butwhataboutthe
callablesresult?Since submit()doesn'twaituntilthetaskcompletes,theexecutorservice
cannotreturntheresultofthecallabledirectly.Insteadtheexecutorreturnsaspecialresult
oftype Futurewhichcanbeusedtoretrievetheactualresultatalaterpointintime.
ExecutorServiceexecutor=Executors.newFixedThreadPool(1);
Future<Integer>future=executor.submit(task);
System.out.println("futuredone?"+future.isDone());
Integerresult=future.get();
System.out.println("futuredone?"+future.isDone());
System.out.print("result:"+result);
Aftersubmittingthecallabletotheexecutorwefirstcheckifthefuturehasalreadybeen
finishedexecutionvia isDone().I'mprettysurethisisn'tthecasesincetheabovecallable
sleepsforonesecondbeforereturningtheinteger.
Callingthemethod get()blocksthecurrentthreadandwaitsuntilthecallablecompletes
beforereturningtheactualresult 123.Nowthefutureisfinallydoneandweseethe
followingresultontheconsole:
futuredone?false
futuredone?true
result:123
Futuresaretightlycoupledtotheunderlyingexecutorservice.Keepinmindthateverynon-
terminatedfuturewillthrowexceptionsifyoushutdowntheexecutor:
ModernJava-AGuidetoJava8
53Java8ConcurrencyTutorial:ThreadsandExecutors

executor.shutdownNow();
future.get();
Youmighthavenoticedthatthecreationoftheexecutorslightlydiffersfromtheprevious
example.Weuse newFixedThreadPool(1)tocreateanexecutorservicebackedbyathread-
poolofsizeone.Thisisequivalentto newSingleThreadExecutor()butwecouldlater
increasethepoolsizebysimplypassingavaluelargerthanone.
Timeouts
Anycallto future.get()willblockandwaituntiltheunderlyingcallablehasbeen
terminated.Intheworstcaseacallablerunsforever-thusmakingyourapplication
unresponsive.Youcansimplycounteractthosescenariosbypassingatimeout:
ExecutorServiceexecutor=Executors.newFixedThreadPool(1);
Future<Integer>future=executor.submit(()->{
try{
TimeUnit.SECONDS.sleep(2);
return123;
}
catch(InterruptedExceptione){
thrownewIllegalStateException("taskinterrupted",e);
}
});
future.get(1,TimeUnit.SECONDS);
Executingtheabovecoderesultsina TimeoutException:
Exceptioninthread"main"java.util.concurrent.TimeoutException
atjava.util.concurrent.FutureTask.get(FutureTask.java:205)
Youmightalreadyhaveguessedwhythisexceptionisthrown:Wespecifiedamaximumwait
timeofonesecondbutthecallableactuallyneedstwosecondsbeforereturningtheresult.
InvokeAll
Executorssupportbatchsubmittingofmultiplecallablesatoncevia invokeAll().This
methodacceptsacollectionofcallablesandreturnsalistoffutures.
ModernJava-AGuidetoJava8
54Java8ConcurrencyTutorial:ThreadsandExecutors

ExecutorServiceexecutor=Executors.newWorkStealingPool();
List<Callable<String>>callables=Arrays.asList(
()->"task1",
()->"task2",
()->"task3");
executor.invokeAll(callables)
.stream()
.map(future->{
try{
returnfuture.get();
}
catch(Exceptione){
thrownewIllegalStateException(e);
}
})
.forEach(System.out::println);
InthisexampleweutilizeJava8functionalstreamsinordertoprocessallfuturesreturned
bytheinvocationof invokeAll.Wefirstmapeachfuturetoitsreturnvalueandthenprint
eachvaluetotheconsole.Ifyou'renotyetfamiliarwithstreamsreadmyJava8Stream
Tutorial.
InvokeAny
Anotherwayofbatch-submittingcallablesisthemethod invokeAny()whichworksslightly
differentto invokeAll().Insteadofreturningfutureobjectsthismethodblocksuntilthefirst
callableterminatesandreturnstheresultofthatcallable.
Inordertotestthisbehaviorweusethishelpermethodtosimulatecallableswithdifferent
durations.Themethodreturnsacallablethatsleepsforacertainamountoftimeuntil
returningthegivenresult:
Callable<String>callable(Stringresult,longsleepSeconds){
return()->{
TimeUnit.SECONDS.sleep(sleepSeconds);
returnresult;
};
}
Weusethismethodtocreateabunchofcallableswithdifferentdurationsfromonetothree
seconds.Submittingthosecallablestoanexecutorvia invokeAny()returnsthestringresult
ofthefastestcallable-inthatcasetask2:
ModernJava-AGuidetoJava8
55Java8ConcurrencyTutorial:ThreadsandExecutors

ExecutorServiceexecutor=Executors.newWorkStealingPool();
List<Callable<String>>callables=Arrays.asList(
callable("task1",2),
callable("task2",1),
callable("task3",3));
Stringresult=executor.invokeAny(callables);
System.out.println(result);
//=>task2
Theaboveexampleusesyetanothertypeofexecutorcreatedvia newWorkStealingPool().
ThisfactorymethodispartofJava8andreturnsanexecutoroftype ForkJoinPoolwhich
worksslightlydifferentthannormalexecutors.Insteadofusingafixedsizethread-pool
ForkJoinPoolsarecreatedforagivenparallelismsizewhichperdefaultisthenumberof
availablecoresofthehostsCPU.
ForkJoinPoolsexistsinceJava7andwillbecoveredindetailinalatertutorialofthisseries.
Let'sfinishthistutorialbytakingadeeperlookatscheduledexecutors.
ScheduledExecutors
We'vealreadylearnedhowtosubmitandruntasksonceonanexecutor.Inorderto
periodicallyruncommontasksmultipletimes,wecanutilizescheduledthreadpools.
A ScheduledExecutorServiceiscapableofschedulingtaskstoruneitherperiodicallyoronce
afteracertainamountoftimehaselapsed.
Thiscodesampleschedulesatasktorunafteraninitialdelayofthreesecondshaspassed:
ScheduledExecutorServiceexecutor=Executors.newScheduledThreadPool(1);
Runnabletask=()->System.out.println("Scheduling:"+System.nanoTime());
ScheduledFuture<?>future=executor.schedule(task,3,TimeUnit.SECONDS);
TimeUnit.MILLISECONDS.sleep(1337);
longremainingDelay=future.getDelay(TimeUnit.MILLISECONDS);
System.out.printf("RemainingDelay:%sms",remainingDelay);
Schedulingataskproducesaspecializedfutureoftype ScheduledFuturewhich-inaddition
to Future-providesthemethod getDelay()toretrievetheremainingdelay.Afterthis
delayhaselapsedthetaskwillbeexecutedconcurrently.
Inordertoscheduletaskstobeexecutedperiodically,executorsprovidethetwomethods
scheduleAtFixedRate()and scheduleWithFixedDelay().Thefirstmethodiscapableof
executingtaskswithafixedtimerate,e.g.onceeverysecondasdemonstratedinthis
example:
ModernJava-AGuidetoJava8
56Java8ConcurrencyTutorial:ThreadsandExecutors

ScheduledExecutorServiceexecutor=Executors.newScheduledThreadPool(1);
Runnabletask=()->System.out.println("Scheduling:"+System.nanoTime());
intinitialDelay=0;
intperiod=1;
executor.scheduleAtFixedRate(task,initialDelay,period,TimeUnit.SECONDS);
Additionallythismethodacceptsaninitialdelaywhichdescribestheleadingwaittimebefore
thetaskwillbeexecutedforthefirsttime.
Pleasekeepinmindthat scheduleAtFixedRate()doesn'ttakeintoaccounttheactual
durationofthetask.Soifyouspecifyaperiodofonesecondbutthetaskneeds2seconds
tobeexecutedthenthethreadpoolwillworkingtocapacityverysoon.
Inthatcaseyoushouldconsiderusing scheduleWithFixedDelay()instead.Thismethod
worksjustlikethecounterpartdescribedabove.Thedifferenceisthatthewaittimeperiod
appliesbetweentheendofataskandthestartofthenexttask.Forexample:
ScheduledExecutorServiceexecutor=Executors.newScheduledThreadPool(1);
Runnabletask=()->{
try{
TimeUnit.SECONDS.sleep(2);
System.out.println("Scheduling:"+System.nanoTime());
}
catch(InterruptedExceptione){
System.err.println("taskinterrupted");
}
};
executor.scheduleWithFixedDelay(task,0,1,TimeUnit.SECONDS);
Thisexampleschedulesataskwithafixeddelayofonesecondbetweentheendofan
executionandthestartofthenextexecution.Theinitialdelayiszeroandthetasksduration
istwoseconds.Soweendupwithanexecutionintervalof0s,3s,6s,9sandsoon.Asyou
cansee scheduleWithFixedDelay()ishandyifyoucannotpredictthedurationofthe
scheduledtasks.
Thiswasthefirstpartoutofaseriesofconcurrencytutorials.Irecommendpracticingthe
showncodesamplesbyyourown.YoufindallcodesamplesfromthisarticleonGitHub,so
feelfreetoforktherepoandgivemestar.
Ihopeyou'veenjoyedthisarticle.Ifyouhaveanyfurtherquestionssendmeyourfeedback
inthecommentsbeloworviaTwitter.
Part1:ThreadsandExecutors
Part2:SynchronizationandLocks
Part3:AtomicVariablesandConcurrentMap
ModernJava-AGuidetoJava8
57Java8ConcurrencyTutorial:ThreadsandExecutors

Java8ConcurrencyTutorial:Synchronization
andLocks
April30,2015
WelcometothesecondpartofmyJava8ConcurrencyTutorialoutofaseriesofguides
teachingmulti-threadedprogramminginJava8witheasilyunderstoodcodeexamples.In
thenext15minyoulearnhowtosynchronizeaccesstomutablesharedvariablesviathe
synchronizedkeyword,locksandsemaphores.
Part1:ThreadsandExecutors
Part2:SynchronizationandLocks
Part3:AtomicVariablesandConcurrentMap
ThemajorityofconceptsshowninthisarticlealsoworkinolderversionsofJava.However
thecodesamplesfocusonJava8andmakeheavyuseoflambdaexpressionsandnew
concurrencyfeatures.Ifyou'renotyetfamiliarwithlambdasIrecommendreadingmyJava8
Tutorialfirst.
Forsimplicitythecodesamplesofthistutorialmakeuseofthetwohelpermethods
sleep(seconds)and stop(executor)asdefinedhere.
Synchronized
Intheprevioustutorial)we'velearnedhowtoexecutecodeinparallelviaexecutorservices.
Whenwritingsuchmulti-threadedcodeyouhavetopayparticularattentionwhenaccessing
sharedmutablevariablesconcurrentlyfrommultiplethreads.Let'sjustsaywewantto
incrementanintegerwhichisaccessiblesimultaneouslyfrommultiplethreads.
Wedefineafield countwithamethod increment()toincreasecountbyone:
intcount=0;
voidincrement(){
count=count+1;
}
Whencallingthismethodconcurrentlyfrommultiplethreadswe'reinserioustrouble:
ModernJava-AGuidetoJava8
58Java8ConcurrencyTutorial:SynchronizationandLocks

ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
IntStream.range(0,10000)
.forEach(i->executor.submit(this::increment));
stop(executor);
System.out.println(count);//9965
Insteadofseeingaconstantresultcountof10000theactualresultvarieswithevery
executionoftheabovecode.Thereasonisthatweshareamutablevariableupondifferent
threadswithoutsynchronizingtheaccesstothisvariablewhichresultsinaracecondition.
Threestepshavetobeperformedinordertoincrementthenumber:(i)readthecurrent
value,(ii)increasethisvaluebyoneand(iii)writethenewvaluetothevariable.Iftwo
threadsperformthesestepsinparallelit'spossiblethatboththreadsperformstep1
simultaneouslythusreadingthesamecurrentvalue.Thisresultsinlostwritessotheactual
resultislower.Intheabovesample35incrementsgotlostduetoconcurrent
unsynchronizedaccesstocountbutyoumayseedifferentresultswhenexecutingthecode
byyourself.
LuckilyJavasupportsthread-synchronizationsincetheearlydaysviathe synchronized
keyword.Wecanutilize synchronizedtofixtheaboveraceconditionswhenincrementing
thecount:
synchronizedvoidincrementSync(){
count=count+1;
}
Whenusing incrementSync()concurrentlywegetthedesiredresultcountof10000.No
raceconditionsoccuranylongerandtheresultisstablewitheveryexecutionofthecode:
ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
IntStream.range(0,10000)
.forEach(i->executor.submit(this::incrementSync));
stop(executor);
System.out.println(count);//10000
The synchronizedkeywordisalsoavailableasablockstatement.
voidincrementSync(){
synchronized(this){
count=count+1;
}
}
ModernJava-AGuidetoJava8
59Java8ConcurrencyTutorial:SynchronizationandLocks

InternallyJavausesasocalledmonitoralsoknownasmonitorlockorintrinsiclockinorder
tomanagesynchronization.Thismonitorisboundtoanobject,e.g.whenusing
synchronizedmethodseachmethodsharethesamemonitorofthecorrespondingobject.
Allimplicitmonitorsimplementthereentrantcharacteristics.Reentrantmeansthatlocksare
boundtothecurrentthread.Athreadcansafelyacquirethesamelockmultipletimeswithout
runningintodeadlocks(e.g.asynchronizedmethodcallsanothersynchronizedmethodon
thesameobject).
Locks
Insteadofusingimplicitlockingviathe synchronizedkeywordtheConcurrencyAPI
supportsvariousexplicitlocksspecifiedbythe Lockinterface.Lockssupportvarious
methodsforfinergrainedlockcontrolthusaremoreexpressivethanimplicitmonitors.
MultiplelockimplementationsareavailableinthestandardJDKwhichwillbedemonstrated
inthefollowingsections.
ReentrantLock
Theclass ReentrantLockisamutualexclusionlockwiththesamebasicbehaviorasthe
implicitmonitorsaccessedviathe synchronizedkeywordbutwithextendedcapabilities.As
thenamesuggeststhislockimplementsreentrantcharacteristicsjustasimplicitmonitors.
Let'sseehowtheabovesamplelookslikeusing ReentrantLock:
ReentrantLocklock=newReentrantLock();
intcount=0;
voidincrement(){
lock.lock();
try{
count++;
}finally{
lock.unlock();
}
}
Alockisacquiredvia lock()andreleasedvia unlock().It'simportanttowrapyourcode
intoa try/finallyblocktoensureunlockingincaseofexceptions.Thismethodisthread-
safejustlikethesynchronizedcounterpart.Ifanotherthreadhasalreadyacquiredthelock
subsequentcallsto lock()pausethecurrentthreaduntilthelockhasbeenunlocked.Only
onethreadcanholdthelockatanygiventime.
Lockssupportvariousmethodsforfinegrainedcontrolasseeninthenextsample:
ModernJava-AGuidetoJava8
60Java8ConcurrencyTutorial:SynchronizationandLocks

ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
ReentrantLocklock=newReentrantLock();
executor.submit(()->{
lock.lock();
try{
sleep(1);
}finally{
lock.unlock();
}
});
executor.submit(()->{
System.out.println("Locked:"+lock.isLocked());
System.out.println("Heldbyme:"+lock.isHeldByCurrentThread());
booleanlocked=lock.tryLock();
System.out.println("Lockacquired:"+locked);
});
stop(executor);
Whilethefirsttaskholdsthelockforonesecondthesecondtaskobtainsdifferent
informationaboutthecurrentstateofthelock:
Locked:true
Heldbyme:false
Lockacquired:false
Themethod tryLock()asanalternativeto lock()triestoacquirethelockwithoutpausing
thecurrentthread.Thebooleanresultmustbeusedtocheckifthelockhasactuallybeen
acquiredbeforeaccessinganysharedmutablevariables.
ReadWriteLock
Theinterface ReadWriteLockspecifiesanothertypeoflockmaintainingapairoflocksfor
readandwriteaccess.Theideabehindread-writelocksisthatit'susuallysafetoread
mutablevariablesconcurrentlyaslongasnobodyiswritingtothisvariable.Sotheread-lock
canbeheldsimultaneouslybymultiplethreadsaslongasnothreadsholdthewrite-lock.
Thiscanimproveperformanceandthroughputincasethatreadsaremorefrequentthan
writes.
ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
Map<String,String>map=newHashMap<>();
ReadWriteLocklock=newReentrantReadWriteLock();
executor.submit(()->{
lock.writeLock().lock();
try{
sleep(1);
map.put("foo","bar");
}finally{
lock.writeLock().unlock();
}
});
ModernJava-AGuidetoJava8
61Java8ConcurrencyTutorial:SynchronizationandLocks

Theaboveexamplefirstacquiresawrite-lockinordertoputanewvaluetothemapafter
sleepingforonesecond.Beforethistaskhasfinishedtwoothertasksarebeingsubmitted
tryingtoreadtheentryfromthemapandsleepforonesecond:
RunnablereadTask=()->{
lock.readLock().lock();
try{
System.out.println(map.get("foo"));
sleep(1);
}finally{
lock.readLock().unlock();
}
};
executor.submit(readTask);
executor.submit(readTask);
stop(executor);
Whenyouexecutethiscodesampleyou'llnoticethatbothreadtaskshavetowaitthewhole
seconduntilthewritetaskhasfinished.Afterthewritelockhasbeenreleasedbothread
tasksareexecutedinparallelandprinttheresultsimultaneouslytotheconsole.Theydon't
havetowaitforeachothertofinishbecauseread-lockscansafelybeacquiredconcurrently
aslongasnowrite-lockisheldbyanotherthread.
StampedLock
Java8shipswithanewkindoflockcalled StampedLockwhichalsosupportreadandwrite
locksjustlikeintheexampleabove.Incontrastto ReadWriteLockthelockingmethodsofa
StampedLockreturnastamprepresentedbya longvalue.Youcanusethesestampsto
eitherreleasealockortocheckifthelockisstillvalid.Additionallystampedlockssupport
anotherlockmodecalledoptimisticlocking.
Let'srewritethelastexamplecodetouse StampedLockinsteadof ReadWriteLock:
ModernJava-AGuidetoJava8
62Java8ConcurrencyTutorial:SynchronizationandLocks

ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
Map<String,String>map=newHashMap<>();
StampedLocklock=newStampedLock();
executor.submit(()->{
longstamp=lock.writeLock();
try{
sleep(1);
map.put("foo","bar");
}finally{
lock.unlockWrite(stamp);
}
});
RunnablereadTask=()->{
longstamp=lock.readLock();
try{
System.out.println(map.get("foo"));
sleep(1);
}finally{
lock.unlockRead(stamp);
}
};
executor.submit(readTask);
executor.submit(readTask);
stop(executor);
Obtainingareadorwritelockvia readLock()or writeLock()returnsastampwhichislater
usedforunlockingwithinthefinallyblock.Keepinmindthatstampedlocksdon'timplement
reentrantcharacteristics.Eachcalltolockreturnsanewstampandblocksifnolockis
availableevenifthesamethreadalreadyholdsalock.Soyouhavetopayparticular
attentionnottorunintodeadlocks.
Justlikeintheprevious ReadWriteLockexamplebothreadtaskshavetowaituntilthewrite
lockhasbeenreleased.Thenbothreadtasksprinttotheconsolesimultaneouslybecause
multiplereadsdoesn'tblockeachotheraslongasnowrite-lockisheld.
Thenextexampledemonstratesoptimisticlocking:
ModernJava-AGuidetoJava8
63Java8ConcurrencyTutorial:SynchronizationandLocks

ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
StampedLocklock=newStampedLock();
executor.submit(()->{
longstamp=lock.tryOptimisticRead();
try{
System.out.println("OptimisticLockValid:"+lock.validate(stamp));
sleep(1);
System.out.println("OptimisticLockValid:"+lock.validate(stamp));
sleep(2);
System.out.println("OptimisticLockValid:"+lock.validate(stamp));
}finally{
lock.unlock(stamp);
}
});
executor.submit(()->{
longstamp=lock.writeLock();
try{
System.out.println("WriteLockacquired");
sleep(2);
}finally{
lock.unlock(stamp);
System.out.println("Writedone");
}
});
stop(executor);
Anoptimisticreadlockisacquiredbycalling tryOptimisticRead()whichalwaysreturnsa
stampwithoutblockingthecurrentthread,nomatterifthelockisactuallyavailable.Ifthere's
alreadyawritelockactivethereturnedstampequalszero.Youcanalwayscheckifastamp
isvalidbycalling lock.validate(stamp).
Executingtheabovecoderesultsinthefollowingoutput:
OptimisticLockValid:true
WriteLockacquired
OptimisticLockValid:false
Writedone
OptimisticLockValid:false
Theoptimisticlockisvalidrightafteracquiringthelock.Incontrasttonormalreadlocksan
optimisticlockdoesn'tpreventotherthreadstoobtainawritelockinstantaneously.After
sendingthefirstthreadtosleepforonesecondthesecondthreadobtainsawritelock
withoutwaitingfortheoptimisticreadlocktobereleased.Fromthispointtheoptimisticread
lockisnolongervalid.Evenwhenthewritelockisreleasedtheoptimisticreadlocksstays
invalid.
Sowhenworkingwithoptimisticlocksyouhavetovalidatethelockeverytimeafter
accessinganysharedmutablevariabletomakesurethereadwasstillvalid.
Sometimesit'susefultoconvertareadlockintoawritelockwithoutunlockingandlocking
again. StampedLockprovidesthemethod tryConvertToWriteLock()forthatpurposeasseen
inthenextsample:
ModernJava-AGuidetoJava8
64Java8ConcurrencyTutorial:SynchronizationandLocks

ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
StampedLocklock=newStampedLock();
executor.submit(()->{
longstamp=lock.readLock();
try{
if(count==0){
stamp=lock.tryConvertToWriteLock(stamp);
if(stamp==0L){
System.out.println("Couldnotconverttowritelock");
stamp=lock.writeLock();
}
count=23;
}
System.out.println(count);
}finally{
lock.unlock(stamp);
}
});
stop(executor);
Thetaskfirstobtainsareadlockandprintsthecurrentvalueoffield counttotheconsole.
Butifthecurrentvalueiszerowewanttoassignanewvalueof 23.Wefirsthaveto
convertthereadlockintoawritelocktonotbreakpotentialconcurrentaccessbyother
threads.Calling tryConvertToWriteLock()doesn'tblockbutmayreturnazerostamp
indicatingthatnowritelockiscurrentlyavailable.Inthatcasewecall writeLock()toblock
thecurrentthreaduntilawritelockisavailable.
Semaphores
InadditiontolockstheConcurrencyAPIalsosupportscountingsemaphores.Whereaslocks
usuallygrantexclusiveaccesstovariablesorresources,asemaphoreiscapableof
maintainingwholesetsofpermits.Thisisusefulindifferentscenarioswhereyouhaveto
limittheamountconcurrentaccesstocertainpartsofyourapplication.
Here'sanexamplehowtolimitaccesstoalongrunningtasksimulatedby sleep(5):
ModernJava-AGuidetoJava8
65Java8ConcurrencyTutorial:SynchronizationandLocks

ExecutorServiceexecutor=Executors.newFixedThreadPool(10);
Semaphoresemaphore=newSemaphore(5);
RunnablelongRunningTask=()->{
booleanpermit=false;
try{
permit=semaphore.tryAcquire(1,TimeUnit.SECONDS);
if(permit){
System.out.println("Semaphoreacquired");
sleep(5);
}else{
System.out.println("Couldnotacquiresemaphore");
}
}catch(InterruptedExceptione){
thrownewIllegalStateException(e);
}finally{
if(permit){
semaphore.release();
}
}
}
IntStream.range(0,10)
.forEach(i->executor.submit(longRunningTask));
stop(executor);
Theexecutorcanpotentiallyrun10tasksconcurrentlybutweuseasemaphoreofsize5,
thuslimitingconcurrentaccessto5.It'simportanttousea try/finallyblocktoproperly
releasethesemaphoreevenincaseofexceptions.
Executingtheabovecoderesultsinthefollowingoutput:
Semaphoreacquired
Semaphoreacquired
Semaphoreacquired
Semaphoreacquired
Semaphoreacquired
Couldnotacquiresemaphore
Couldnotacquiresemaphore
Couldnotacquiresemaphore
Couldnotacquiresemaphore
Couldnotacquiresemaphore
Thesemaphorespermitsaccesstotheactuallongrunningoperationsimulatedby
sleep(5)uptoamaximumof5.Everysubsequentcallto tryAcquire()elapsesthe
maximumwaittimeoutofonesecond,resultingintheappropriateconsoleoutputthatno
semaphorecouldbeacquired.
Thiswasthesecondpartoutofaseriesofconcurrencytutorials.Morepartswillbereleased
inthenearfuture,sostaytuned.Asusualyoufindallcodesamplesfromthisarticleon
GitHub,sofeelfreetoforktherepoandtryitbyyourown.
Ihopeyou'veenjoyedthisarticle.Ifyouhaveanyfurtherquestionssendmeyourfeedback
inthecommentsbelow.YoushouldalsofollowmeonTwitterformoredev-relatedstuff!
ModernJava-AGuidetoJava8
66Java8ConcurrencyTutorial:SynchronizationandLocks

Part1:ThreadsandExecutors
Part2:SynchronizationandLocks
Part3:AtomicVariablesandConcurrentMap
ModernJava-AGuidetoJava8
67Java8ConcurrencyTutorial:SynchronizationandLocks

Java8ConcurrencyTutorial:AtomicVariables
andConcurrentMap
May22,2015
Welcometothethirdpartofmytutorialseriesaboutmulti-threadedprogramminginJava8.
ThistutorialcoverstwoimportantpartsoftheConcurrencyAPI:AtomicVariablesand
ConcurrentMaps.Bothhavebeengreatlyimprovedwiththeintroductionoflambda
expressionsandfunctionalprogramminginthelatestJava8release.Allthosenewfeatures
aredescribedwithabunchofeasilyunderstoodcodesamples.Enjoy!
Part1:ThreadsandExecutors
Part2:SynchronizationandLocks
Part3:AtomicVariablesandConcurrentMap
Forsimplicitythecodesamplesofthistutorialmakeuseofthetwohelpermethods
sleep(seconds)and stop(executor)asdefinedhere.
AtomicInteger
Thepackage java.concurrent.atomiccontainsmanyusefulclassestoperformatomic
operations.Anoperationisatomicwhenyoucansafelyperformtheoperationinparallelon
multiplethreadswithoutusingthe synchronizedkeywordorlocksasshowninmyprevious
tutorial.
Internally,theatomicclassesmakeheavyuseofcompare-and-swap(CAS),anatomic
instructiondirectlysupportedbymostmodernCPUs.Thoseinstructionsusuallyaremuch
fasterthansynchronizingvialocks.Somyadviceistopreferatomicclassesoverlocksin
caseyoujusthavetochangeasinglemutablevariableconcurrently.
Nowlet'spickoneoftheatomicclassesforafewexamples: AtomicInteger
AtomicIntegeratomicInt=newAtomicInteger(0);
ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
IntStream.range(0,1000)
.forEach(i->executor.submit(atomicInt::incrementAndGet));
stop(executor);
System.out.println(atomicInt.get());//=>1000
ModernJava-AGuidetoJava8
68Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap

Byusing AtomicIntegerasareplacementfor Integerwe'reabletoincrementthenumber
concurrentlyinathread-safemanorwithoutsynchronizingtheaccesstothevariable.The
method incrementAndGet()isanatomicoperationsowecansafelycallthismethodfrom
multiplethreads.
AtomicIntegersupportsvariouskindsofatomicoperations.Themethod updateAndGet()
acceptsalambdaexpressioninordertoperformarbitraryarithmeticoperationsuponthe
integer:
AtomicIntegeratomicInt=newAtomicInteger(0);
ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
IntStream.range(0,1000)
.forEach(i->{
Runnabletask=()->
atomicInt.updateAndGet(n->n+2);
executor.submit(task);
});
stop(executor);
System.out.println(atomicInt.get());//=>2000
Themethod accumulateAndGet()acceptsanotherkindoflambdaexpressionoftype
IntBinaryOperator.Weusethismethodtosumupallvaluesfrom0to1000concurrentlyin
thenextsample:
AtomicIntegeratomicInt=newAtomicInteger(0);
ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
IntStream.range(0,1000)
.forEach(i->{
Runnabletask=()->
atomicInt.accumulateAndGet(i,(n,m)->n+m);
executor.submit(task);
});
stop(executor);
System.out.println(atomicInt.get());//=>499500
OtherusefulatomicclassesareAtomicBoolean,AtomicLongandAtomicReference.
LongAdder
Theclass LongAdderasanalternativeto AtomicLongcanbeusedtoconsecutivelyadd
valuestoanumber.
ModernJava-AGuidetoJava8
69Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap

ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
IntStream.range(0,1000)
.forEach(i->executor.submit(adder::increment));
stop(executor);
System.out.println(adder.sumThenReset());//=>1000
LongAdderprovidesmethods add()and increment()justliketheatomicnumberclasses
andisalsothread-safe.Butinsteadofsummingupasingleresultthisclassmaintainsaset
ofvariablesinternallytoreducecontentionoverthreads.Theactualresultcanberetrieved
bycalling sum()or sumThenReset().
Thisclassisusuallypreferableoveratomicnumberswhenupdatesfrommultiplethreads
aremorecommonthanreads.Thisisoftenthecasewhencapturingstatisticaldata,e.g.you
wanttocountthenumberofrequestsservedonawebserver.Thedrawbackof LongAdder
ishighermemoryconsumptionbecauseasetofvariablesisheldin-memory.
LongAccumulator
LongAccumulatorisamoregeneralizedversionofLongAdder.Insteadofperformingsimple
addoperationstheclass LongAccumulatorbuildsaroundalambdaexpressionoftype
LongBinaryOperatorasdemonstratedinthiscodesample:
LongBinaryOperatorop=(x,y)->2*x+y;
LongAccumulatoraccumulator=newLongAccumulator(op,1L);
ExecutorServiceexecutor=Executors.newFixedThreadPool(2);
IntStream.range(0,10)
.forEach(i->executor.submit(()->accumulator.accumulate(i)));
stop(executor);
System.out.println(accumulator.getThenReset());//=>2539
WecreateaLongAccumulatorwiththefunction 2*x+yandaninitialvalueofone.With
everycallto accumulate(i)boththecurrentresultandthevalue iarepassedas
parameterstothelambdaexpression.
A LongAccumulatorjustlike LongAddermaintainsasetofvariablesinternallytoreduce
contentionoverthreads.
ConcurrentMap
ModernJava-AGuidetoJava8
70Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap

Theinterface ConcurrentMapextendsthemapinterfaceanddefinesoneofthemostuseful
concurrentcollectiontypes.Java8introducesfunctionalprogrammingbyaddingnew
methodstothisinterface.
Inthenextcodesnippetsweusethefollowingsamplemaptodemonstratesthosenew
methods:
ConcurrentMap<String,String>map=newConcurrentHashMap<>();
map.put("foo","bar");
map.put("han","solo");
map.put("r2","d2");
map.put("c3","p0");
Themethod forEach()acceptsalambdaexpressionoftype BiConsumerwithboththekey
andvalueofthemappassedasparameters.Itcanbeusedasareplacementtofor-each
loopstoiterateovertheentriesoftheconcurrentmap.Theiterationisperformed
sequentiallyonthecurrentthread.
map.forEach((key,value)->System.out.printf("%s=%s\n",key,value));
Themethod putIfAbsent()putsanewvalueintothemaponlyifnovalueexistsforthe
givenkey.Atleastforthe ConcurrentHashMapimplementationofthismethodisthread-safe
justlike put()soyoudon'thavetosynchronizewhenaccessingthemapconcurrentlyfrom
differentthreads:
Stringvalue=map.putIfAbsent("c3","p1");
System.out.println(value);//p0
Themethod getOrDefault()returnsthevalueforthegivenkey.Incasenoentryexistsfor
thiskeythepasseddefaultvalueisreturned:
Stringvalue=map.getOrDefault("hi","there");
System.out.println(value);//there
Themethod replaceAll()acceptsalambdaexpressionoftype BiFunction.BiFunctions
taketwoparametersandreturnasinglevalue.Inthiscasethefunctioniscalledwiththekey
andthevalueofeachmapentryandreturnsanewvaluetobeassignedforthecurrentkey:
map.replaceAll((key,value)->"r2".equals(key)?"d3":value);
System.out.println(map.get("r2"));//d3
Insteadofreplacingallvaluesofthemap compute()let'sustransformasingleentry.The
methodacceptsboththekeytobecomputedandabi-functiontospecifythetransformation
ofthevalue.
ModernJava-AGuidetoJava8
71Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap

map.compute("foo",(key,value)->value+value);
System.out.println(map.get("foo"));//barbar
Inadditionto compute()twovariantsexist: computeIfAbsent()and computeIfPresent().
Thefunctionalparametersofthesemethodsonlygetcalledifthekeyisabsentorpresent
respectively.
Finally,themethod merge()canbeutilizedtounifyanewvaluewithanexistingvaluein
themap.Mergeacceptsakey,thenewvaluetobemergedintotheexistingentryandabi-
functiontospecifythemergingbehaviorofbothvalues:
map.merge("foo","boo",(oldVal,newVal)->newVal+"was"+oldVal);
System.out.println(map.get("foo"));//boowasfoo
ConcurrentHashMap
Allthosemethodsabovearepartofthe ConcurrentMapinterface,therebyavailabletoall
implementationsofthatinterface.Inadditionthemostimportantimplementation
ConcurrentHashMaphasbeenfurtherenhancedwithacoupleofnewmethodstoperform
paralleloperationsuponthemap.
Justlikeparallelstreamsthosemethodsuseaspecial ForkJoinPoolavailablevia
ForkJoinPool.commonPool()inJava8.Thispoolusesapresetparallelismwhichdependson
thenumberofavailablecores.FourCPUcoresareavailableonmymachinewhichresultsin
aparallelismofthree:
System.out.println(ForkJoinPool.getCommonPoolParallelism());//3
ThisvaluecanbedecreasedorincreasedbysettingthefollowingJVMparameter:
-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
Weusethesameexamplemapfordemonstratingpurposesbutthistimeweworkuponthe
concreteimplementation ConcurrentHashMapinsteadoftheinterface ConcurrentMap,sowe
canaccessallpublicmethodsfromthisclass:
ConcurrentHashMap<String,String>map=newConcurrentHashMap<>();
map.put("foo","bar");
map.put("han","solo");
map.put("r2","d2");
map.put("c3","p0");
ModernJava-AGuidetoJava8
72Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap

Java8introducesthreekindsofparalleloperations: forEach, searchand reduce.Each
ofthoseoperationsareavailableinfourformsacceptingfunctionswithkeys,values,entries
andkey-valuepairarguments.
Allofthosemethodsuseacommonfirstargumentcalled parallelismThreshold.This
thresholdindicatestheminimumcollectionsizewhentheoperationshouldbeexecutedin
parallel.E.g.ifyoupassathresholdof500andtheactualsizeofthemapis499the
operationwillbeperformedsequentiallyonasinglethread.Inthenextexamplesweusea
thresholdofonetoalwaysforceparallelexecutionfordemonstratingpurposes.
ForEach
Themethod forEach()iscapableofiteratingoverthekey-valuepairsofthemapin
parallel.Thelambdaexpressionoftype BiConsumeriscalledwiththekeyandvalueofthe
currentiterationstep.Inordertovisualizeparallelexecutionweprintthecurrentthreads
nametotheconsole.Keepinmindthatinmycasetheunderlying ForkJoinPoolusesupto
amaximumofthreethreads.
map.forEach(1,(key,value)->
System.out.printf("key:%s;value:%s;thread:%s\n",
key,value,Thread.currentThread().getName()));
//key:r2;value:d2;thread:main
//key:foo;value:bar;thread:ForkJoinPool.commonPool-worker-1
//key:han;value:solo;thread:ForkJoinPool.commonPool-worker-2
//key:c3;value:p0;thread:main
Search
Themethod search()acceptsa BiFunctionreturninganon-nullsearchresultforthe
currentkey-valuepairor nullifthecurrentiterationdoesn'tmatchthedesiredsearch
criteria.Assoonasanon-nullresultisreturnedfurtherprocessingissuppressed.Keepin
mindthat ConcurrentHashMapisunordered.Thesearchfunctionshouldnotdependonthe
actualprocessingorderofthemap.Ifmultipleentriesofthemapmatchthegivensearch
functiontheresultmaybenon-deterministic.
Stringresult=map.search(1,(key,value)->{
System.out.println(Thread.currentThread().getName());
if("foo".equals(key)){
returnvalue;
}
returnnull;
});
System.out.println("Result:"+result);
//ForkJoinPool.commonPool-worker-2
//main
//ForkJoinPool.commonPool-worker-3
//Result:bar
ModernJava-AGuidetoJava8
73Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap

Here'sanotherexamplesearchingsolelyonthevaluesofthemap:
Stringresult=map.searchValues(1,value->{
System.out.println(Thread.currentThread().getName());
if(value.length()>3){
returnvalue;
}
returnnull;
});
System.out.println("Result:"+result);
//ForkJoinPool.commonPool-worker-2
//main
//main
//ForkJoinPool.commonPool-worker-1
//Result:solo
Reduce
Themethod reduce()alreadyknownfromJava8Streamsacceptstwolambda
expressionsoftype BiFunction.Thefirstfunctiontransformseachkey-valuepairintoa
singlevalueofanytype.Thesecondfunctioncombinesallthosetransformedvaluesintoa
singleresult,ignoringanypossible nullvalues.
Stringresult=map.reduce(1,
(key,value)->{
System.out.println("Transform:"+Thread.currentThread().getName());
returnkey+"="+value;
},
(s1,s2)->{
System.out.println("Reduce:"+Thread.currentThread().getName());
returns1+","+s2;
});
System.out.println("Result:"+result);
//Transform:ForkJoinPool.commonPool-worker-2
//Transform:main
//Transform:ForkJoinPool.commonPool-worker-3
//Reduce:ForkJoinPool.commonPool-worker-3
//Transform:main
//Reduce:main
//Reduce:main
//Result:r2=d2,c3=p0,han=solo,foo=bar
Ihopeyou'veenjoyedreadingthethirdpartofmytutorialseriesaboutJava8Concurrency.
ThecodesamplesfromthistutorialarehostedonGitHubalongwithmanyotherJava8
codesnippets.You'rewelcometoforktherepoandtryitbyyourown.
Ifyouwanttosupportmywork,pleasesharethistutorialwithyourfriends.Youshouldalso
followmeonTwitterasIconstantlytweetaboutJavaandprogrammingrelatedstuff.
Part1:ThreadsandExecutors
Part2:SynchronizationandLocks
Part3:AtomicVariablesandConcurrentMap
ModernJava-AGuidetoJava8
74Java8ConcurrencyTutorial:AtomicVariablesandConcurrentMap

Java8APIbyExample:Strings,Numbers,
MathandFiles
March25,2015
PlentyoftutorialsandarticlescoverthemostimportantchangesinJava8likelambda
expressionsandfunctionalstreams.Butfurthermoremanyexistingclasseshavebeen
enhancedintheJDK8APIwithusefulfeaturesandmethods.
ThisarticlecoverssomeofthosesmallerchangesintheJava8API-eachdescribedwith
easilyunderstoodcodesamples.Let'stakeadeeperlookintoStrings,Numbers,Mathand
Files.
SlicingStrings
TwonewmethodsareavailableontheStringclass: joinand chars.Thefirstmethod
joinsanynumberofstringsintoasinglestringwiththegivendelimiter:
String.join(":","foobar","foo","bar");
//=>foobar:foo:bar
Thesecondmethod charscreatesastreamforallcharactersofthestring,soyoucanuse
streamoperationsuponthosecharacters:
"foobar:foo:bar"
.chars()
.distinct()
.mapToObj(c->String.valueOf((char)c))
.sorted()
.collect(Collectors.joining());
//=>:abfor
Notonlystringsbutalsoregexpatternsnowbenefitfromstreams.Insteadofsplittingstrings
intostreamsforeachcharacterwecansplitstringsforanypatternandcreateastreamto
workuponasshowninthisexample:
Pattern.compile(":")
.splitAsStream("foobar:foo:bar")
.filter(s->s.contains("bar"))
.sorted()
.collect(Collectors.joining(":"));
//=>bar:foobar
Additionallyregexpatternscanbeconvertedintopredicates.Thosepredicatescanfor
examplebeusedtofilterastreamofstrings:
ModernJava-AGuidetoJava8
75Java8APIbyExample:Strings,Numbers,MathandFiles

Patternpattern=Pattern.compile(".*@gmail\\.com");
Stream.of("bob@gmail.com","alice@hotmail.com")
.filter(pattern.asPredicate())
.count();
//=>1
Theabovepatternacceptsanystringwhichendswith @gmail.comandisthenusedasa
Java8 Predicatetofilterastreamofemailaddresses.
CrunchingNumbers
Java8addsadditionalsupportforworkingwithunsignednumbers.NumbersinJavahad
alwaysbeensigned.Let'slookat Integerforexample:
An intrepresentsamaximumof2³²binarydigits.NumbersinJavaareperdefaultsigned,
sothelastbinarydigitrepresentsthesign(0=positive,1=negative).Thusthemaximum
positivesigned intis2³¹-1startingwiththedecimalzero.
Youcanaccessthisvaluevia Integer.MAX_VALUE:
System.out.println(Integer.MAX_VALUE);//2147483647
System.out.println(Integer.MAX_VALUE+1);//-2147483648
Java8addssupportforparsingunsignedints.Let'sseehowthisworks:
longmaxUnsignedInt=(1l<<32)-1;
Stringstring=String.valueOf(maxUnsignedInt);
intunsignedInt=Integer.parseUnsignedInt(string,10);
Stringstring2=Integer.toUnsignedString(unsignedInt,10);
Asyoucanseeit'snowpossibletoparsethemaximumpossibleunsignednumber2³²-1
intoaninteger.Andyoucanalsoconvertthisnumberbackintoastringrepresentingthe
unsignednumber.
Thiswasn'tpossiblebeforewith parseIntasthisexampledemonstrates:
try{
Integer.parseInt(string,10);
}
catch(NumberFormatExceptione){
System.err.println("couldnotparsesignedintof"+maxUnsignedInt);
}
Thenumberisnotparseableasasignedintbecauseitexceedsthemaximumof2³¹-1.
DotheMath
ModernJava-AGuidetoJava8
76Java8APIbyExample:Strings,Numbers,MathandFiles

Theutilityclass Mathhasbeenenhancedbyacoupleofnewmethodsforhandlingnumber
overflows.Whatdoesthatmean?We'vealreadyseenthatallnumbertypeshavea
maximumvalue.Sowhathappenswhentheresultofanarithmeticoperationdoesn'tfitinto
itssize?
System.out.println(Integer.MAX_VALUE);//2147483647
System.out.println(Integer.MAX_VALUE+1);//-2147483648
Asyoucanseeasocalledintegeroverflowhappenswhichisnormallynotthedesired
behavior.
Java8addssupportforstrictmathtohandlethisproblem. Mathhasbeenextendedbya
coupleofmethodswhoallendswith exact,e.g. addExact.Thosemethodshandle
overflowsproperlybythrowingan ArithmeticExceptionwhentheresultoftheoperation
doesn'tfitintothenumbertype:
try{
Math.addExact(Integer.MAX_VALUE,1);
}
catch(ArithmeticExceptione){
System.err.println(e.getMessage());
//=>integeroverflow
}
Thesameexceptionmightbethrownwhentryingtoconvertlongstointvia toIntExact:
try{
Math.toIntExact(Long.MAX_VALUE);
}
catch(ArithmeticExceptione){
System.err.println(e.getMessage());
//=>integeroverflow
}
WorkingwithFiles
Theutilityclass FileswasfirstintroducedinJava7aspartofJavaNIO.TheJDK8API
addsacoupleofadditionalmethodswhichenablesustousefunctionalstreamswithfiles.
Let'sdeep-diveintoacoupleofcodesamples.
Listingfiles
Themethod Files.liststreamsallpathsforagivendirectory,sowecanusestream
operationslike filterand sorteduponthecontentsofthefilesystem.
ModernJava-AGuidetoJava8
77Java8APIbyExample:Strings,Numbers,MathandFiles

try(Stream<Path>stream=Files.list(Paths.get(""))){
Stringjoined=stream
.map(String::valueOf)
.filter(path->!path.startsWith("."))
.sorted()
.collect(Collectors.joining(";"));
System.out.println("List:"+joined);
}
Theaboveexamplelistsallfilesforthecurrentworkingdirectory,thenmapseachpathtoit's
stringrepresentation.Theresultisthenfiltered,sortedandfinallyjoinedintoastring.If
you'renotyetfamiliarwithfunctionalstreamsyoushouldreadmyJava8StreamTutorial.
Youmighthavenoticedthatthecreationofthestreamiswrappedintoatry/withstatement.
Streamsimplement AutoCloseableandinthiscasewereallyhavetoclosethestream
explicitlysinceit'sbackedbyIOoperations.
>ThereturnedstreamencapsulatesaDirectoryStream.Iftimelydisposaloffilesystem
resourcesisrequired,thetry-with-resourcesconstructshouldbeusedtoensurethatthe
stream'sclosemethodisinvokedafterthestreamoperationsarecompleted.
Findingfiles
Thenextexampledemonstrateshowtofindfilesinadirectoryorit'ssub-directories.
Pathstart=Paths.get("");
intmaxDepth=5;
try(Stream<Path>stream=Files.find(start,maxDepth,(path,attr)->
String.valueOf(path).endsWith(".js"))){
Stringjoined=stream
.sorted()
.map(String::valueOf)
.collect(Collectors.joining(";"));
System.out.println("Found:"+joined);
}
Themethod findacceptsthreearguments:Thedirectorypath startistheinitialstarting
pointand maxDepthdefinesthemaximumfolderdepthtobesearched.Thethirdargument
isamatchingpredicateanddefinesthesearchlogic.Intheaboveexamplewesearchforall
JavaScriptfiles(filenameendswith.js).
Wecanachievethesamebehaviorbyutilizingthemethod Files.walk.Insteadofpassing
asearchpredicatethismethodjustwalksoveranyfile.
ModernJava-AGuidetoJava8
78Java8APIbyExample:Strings,Numbers,MathandFiles

Pathstart=Paths.get("");
intmaxDepth=5;
try(Stream<Path>stream=Files.walk(start,maxDepth)){
Stringjoined=stream
.map(String::valueOf)
.filter(path->path.endsWith(".js"))
.sorted()
.collect(Collectors.joining(";"));
System.out.println("walk():"+joined);
}
Inthisexampleweusethestreamoperation filtertoachievethesamebehaviorasinthe
previousexample.
Readingandwritingfiles
ReadingtextfilesintomemoryandwritingstringsintoatextfileinJava8isfinallyasimple
task.Nomessingaroundwithreadersandwriters.Themethod Files.readAllLinesreads
alllinesofagivenfileintoalistofstrings.Youcansimplymodifythislistandwritethelines
intoanotherfilevia Files.write:
List<String>lines=Files.readAllLines(Paths.get("res/nashorn1.js"));
lines.add("print('foobar');");
Files.write(Paths.get("res/nashorn1-modified.js"),lines);
Pleasekeepinmindthatthosemethodsarenotverymemory-efficientbecausethewhole
filewillbereadintomemory.Thelargerthefilethemoreheap-sizewillbeused.
Asanmemory-efficientalternativeyoucouldusethemethod Files.lines.Insteadof
readingalllinesintomemoryatonce,thismethodreadsandstreamseachlineonebyone
viafunctionalstreams.
try(Stream<String>stream=Files.lines(Paths.get("res/nashorn1.js"))){
stream
.filter(line->line.contains("print"))
.map(String::trim)
.forEach(System.out::println);
}
Ifyouneedmorefine-grainedcontrolyoucaninsteadconstructanewbufferedreader:
Pathpath=Paths.get("res/nashorn1.js");
try(BufferedReaderreader=Files.newBufferedReader(path)){
System.out.println(reader.readLine());
}
Orincaseyouwanttowritetoafilesimplyconstructabufferedwriterinstead:
ModernJava-AGuidetoJava8
79Java8APIbyExample:Strings,Numbers,MathandFiles

Pathpath=Paths.get("res/output.js");
try(BufferedWriterwriter=Files.newBufferedWriter(path)){
writer.write("print('HelloWorld');");
}
Bufferedreadersalsohaveaccesstofunctionalstreams.Themethod linesconstructa
functionalstreamuponalllinesdenotedbythebufferedreader:
Pathpath=Paths.get("res/nashorn1.js");
try(BufferedReaderreader=Files.newBufferedReader(path)){
longcountPrints=reader
.lines()
.filter(line->line.contains("print"))
.count();
System.out.println(countPrints);
}
SoasyoucanseeJava8providesthreesimplewaystoreadthelinesofatextfile,making
textfilehandlingquiteconvenient.
Unfortunatelyyouhavetoclosefunctionalfilestreamsexplicitlywithtry/withstatements
whichmakesthecodesamplesstillkindacluttered.Iwouldhaveexpectedthatfunctional
streamsauto-closewhencallingaterminaloperationlike countor collectsinceyou
cannotcallterminaloperationstwiceonthesamestreamanyway.
Ihopeyou'veenjoyedthisarticle.AllcodesamplesarehostedonGitHubalongwithplenty
ofothercodesnippetsfromalltheJava8articlesofmyblog.Ifthispostwaskindausefulto
youfeelfreetostartherepoandfollowmeonTwitter.
Keeponcoding!
ModernJava-AGuidetoJava8
80Java8APIbyExample:Strings,Numbers,MathandFiles

AvoidingNullChecksinJava8
March15,2015
Howtopreventthefamous NullPointerExceptioninJava?Thisisoneofthekeyquestions
everyJavabeginnerwillasksoonerorlater.Butalsointermediateandexpertprogrammers
getaroundthiserroreverynowandthen.It'sbyfarthemostprevalentkindoferrorinJava
andmanyotherprogramminglanguagesaswell.
TonyHoare,theinventorofthenullreferenceapologizedin2009anddenotesthiskindof
errorsashisbillion-dollarmistake.
>Icallitmybillion-dollarmistake.Itwastheinventionofthenullreferencein1965.Atthat
time,Iwasdesigningthefirstcomprehensivetypesystemforreferencesinanobject
orientedlanguage(ALGOLW).Mygoalwastoensurethatalluseofreferencesshouldbe
absolutelysafe,withcheckingperformedautomaticallybythecompiler.ButIcouldn'tresist
thetemptationtoputinanullreference,simplybecauseitwassoeasytoimplement.This
hasledtoinnumerableerrors,vulnerabilities,andsystemcrashes,whichhaveprobably
causedabilliondollarsofpainanddamageinthelastfortyyears.
Anyways,wehavetodealwithit.SowhatcanwedotopreventNullPointerExceptionsat
all?Well,theobviousansweristoaddnullchecksallaroundtheplace.Sincenullchecks
arekindacumbersomeandpainfulmanylanguagesaddspecialsyntaxforhandlingnull
checksvianullcoalescingoperators-alsoknownaselvisoperatorinlanguageslikeGroovy
orKotlin.
UnfortunatelyJavadoesn'tprovidesuchasyntacticsugar.Butluckilythingsgetbetterin
JavaVersion8.Thispostdescribesacoupleoftechniqueshowtopreventwritingneedless
nullchecksbyutilizingnewfeaturesofJava8likelambdaexpressions.
ImprovingNullSafetyinJava8
I'vealreadyshowninanotherposthowwecanutilizethe OptionaltypeofJava8to
preventnullchecks.Here'stheexamplecodefromtheoriginalpost.
Assumingwehaveahierarchicalclassstructurelikethis:
ModernJava-AGuidetoJava8
81AvoidingNullChecksinJava8

classOuter{
Nestednested;
NestedgetNested(){
returnnested;
}
}
classNested{
Innerinner;
InnergetInner(){
returninner;
}
}
classInner{
Stringfoo;
StringgetFoo(){
returnfoo;
}
}
Resolvingadeepnestedpathinthisstructurecanbekindaawkward.Wehavetowritea
bunchofnullcheckstomakesurenottoraisea NullPointerException:
Outerouter=newOuter();
if(outer!=null&&outer.nested!=null&&outer.nested.inner!=null){
System.out.println(outer.nested.inner.foo);
}
WecangetridofallthosenullchecksbyutilizingtheJava8 Optionaltype.Themethod
mapacceptsalambdaexpressionoftype Functionandautomaticallywrapseachfunction
resultintoan Optional.Thatenablesustopipemultiple mapoperationsinarow.Null
checksareautomaticallyhandledunderthehood.
Optional.of(newOuter())
.map(Outer::getNested)
.map(Nested::getInner)
.map(Inner::getFoo)
.ifPresent(System.out::println);
Analternativewaytoachievethesamebehaviorisbyutilizingasupplierfunctiontoresolve
thenestedpath:
Outerobj=newOuter();
resolve(()->obj.getNested().getInner().getFoo());
.ifPresent(System.out::println);
Calling obj.getNested().getInner().getFoo())mightthrowa NullPointerException.Inthis
casetheexceptionwillbecaughtandthemethodreturns Optional.empty().
ModernJava-AGuidetoJava8
82AvoidingNullChecksinJava8

publicstatic<T>Optional<T>resolve(Supplier<T>resolver){
try{
Tresult=resolver.get();
returnOptional.ofNullable(result);
}
catch(NullPointerExceptione){
returnOptional.empty();
}
}
Pleasekeepinmindthatbothsolutionsareprobablynotasperformantastraditionalnull
checks.Inmostcasesthatshouldn'tbemuchofanissue.
AsusualtheabovecodesamplesarehostedonGitHub.
Happycoding!
>UPDATE:I'veupdatedthecodesamplesthankstoahintfromZukhrammonReddit.
ModernJava-AGuidetoJava8
83AvoidingNullChecksinJava8

FixingJava8StreamGotchaswithIntelliJ
IDEA
March05,2015
Java8hasbeenreleasedalmostoneyearagoinMarch2014.AtPonduswe'vemanagedto
updateallofourproductionserverstothisnewversionbackinMay2014.Sincethenwe've
migratedmajorpartsofourcodebasetolambdaexpressions,streamsandthenewDate
API.WealsouseNashorntodynamicallyscriptpartsofourapplicationwhichmaychange
duringruntime.
ThemostusedfeaturebesideslambdasisthenewStreamAPI.Collectionoperationsareall
aroundtheplaceinalmostanycodebaseI'veeverseen.AndStreamsareagreatwayto
improvecodereadabilityofallthosecollectioncrunching.
Butonethingaboutstreamsreallybothersme:Streamsonlyprovideafewterminal
operationslike reduceand findFirstdirectlywhileothersareonlyaccessiblevia
collect.There'sautilityclassCollectors,providingabunchofconvenientcollectorslike
toList, toSet, joiningand groupingBy.
Forexamplethiscodefiltersoveracollectionofstringsandcreatesanewlist:
stringCollection
.stream()
.filter(e->e.startsWith("a"))
.collect(Collectors.toList());
Aftermigratingaprojectwith300klinesofcodetostreamsIcansaythat toList, toSet
and groupingByarebyfarthemostusedterminaloperationsinourproject.SoIreally
cannotunderstandthedesigndecisionnottointegratethosemethodsdirectlyintothe
Streaminterfacesoyoucouldjustwrite:
stringCollection
.stream()
.filter(e->e.startsWith("a"))
.toList();
Thismightlooklikeaminorimperfectionatfirstbutitgetsreallyannoyingifyouhavetouse
thiskindofstuffoverandoveragain.
There'samethod toArray()butno toList().SoIreallyhopesomeofthemore
convenientcollectorswillmakeit'swayintothe StreaminterfaceinJava9.Brian?_
ModernJava-AGuidetoJava8
84FixingJava8StreamGotchaswithIntelliJIDEA

>Asasidenote:Stream.jsisaJavaScriptportoftheJava8StreamsAPIforthebrowser
andaddressesthedescribedissuenicely.Allimportantterminaloperationsaredirectly
accessibleonthestreamitselfforconvenience.SeetheAPIdocfordetails.
Anyways.IntelliJIDEAclaimstobethemostintelligentJavaIDE.Solet'sseehowwecan
utilizeIDEAtosolvethisproblemforus.
IntelliJIDEAtotherescue
IntelliJIDEAcomeswithahandyfeaturecalledLiveTemplates.Ifyoudon'talreadyknow
whatitis:LiveTemplatesareshortcutsforcommonlyusedcodesnippets.E.g.youtype
sout+tabulatorandIDEAinsertsthecodesnippet System.out.println().Readhereto
learnmoreaboutit.
HowdoesLiveTemplateshelpwiththeproblemdescribedabove?Actuallywecansimply
createourownLiveTemplatesforallthecommonlyuseddefaultStreamcollectors.E.g.we
cancreateaLiveTemplatewiththeabbreviation .toListtoinserttheappropriatecollector
.collect(Collectors.toList())automatically.
Thisishowitlookslikeinaction:
SetupyourownLiveTemplates
Let'sseehowwecansetthisup.FirstgotoSettingsandchooseLiveTemplatesinthe
menutotheleft.Youcanalsousethehandyfilterinputatthetopleftofthedialog.
ModernJava-AGuidetoJava8
85FixingJava8StreamGotchaswithIntelliJIDEA

Nextwecancreateanewgroupcalled Streamviathe +iconontheright.Nextweaddall
ofourstream-relatedLiveTemplatestothisgroup.I'musingthedefaultcollectors toList,
toSet, groupingByand joinquitecommonly,soIcreateanewLiveTemplateforeachof
thosemethods.
Thispartisimportant:AfteraddinganewLiveTemplateyouhavetospecifytheapplicable
contextatthebottomofthedialog.YouhavetochooseJava→Other.Afterwardsyou
definetheabbreviation,adescriptionandtheactualtemplatecode.
//Abbreviation:.toList
.collect(Collectors.toList())
//Abbreviation:.toSet
.collect(Collectors.toSet())
//Abbreviation:.join
.collect(Collectors.joining("$END$"))
//Abbreviation:.groupBy
.collect(Collectors.groupingBy(e->$END$))
Thespecialvariable $END$determinesthecursorspositionafterusingthetemplate,soyou
candirectlystarttypingatthisposition,e.g.todefinethejoiningdelimiter.
ModernJava-AGuidetoJava8
86FixingJava8StreamGotchaswithIntelliJIDEA

>Hint:Youshouldenabletheoption"Addunambiguousimportsonthefly"soIDEA
automaticallyaddsanimportstatementto java.util.stream.Collectors.Theoptionis
locatedin:Editor→General→AutoImport
Let'sseethosetwotemplatesinaction:
Join
GroupBy
LiveTemplatesinIntelliJIDEAareanextremelyversatileandpowerfultool.Youcangreatly
increaseyourcodingproductivitywithit.DoyouknowotherexampleswhereLiveTemplates
cansaveyourlive?Letmeknow!
Stillnotsatisfied?LearneverythingyoueverwantedtoknowaboutJava8Streamsinmy
StreamsTutorial.
Happycoding.
ModernJava-AGuidetoJava8
87FixingJava8StreamGotchaswithIntelliJIDEA

UsingBackbone.jswithNashorn
April07,2014
ThisexampledemonstrateshowtouseBackbone.jsmodelswiththeJava8Nashorn
JavascriptEngine.FirstreleasedinMarch2014aspartofJavaSE8,Nashornextends
JavascapabilitiesbyrunningjavascriptcodenativelyontheJVM.Forjavawebdevelopers
Nashornmightbeespeciallyusefulforre-usingexistingclient-sidecodeonthejavaserver.
TraditionallyNode.jswasinaclearadvantage,butNashornspossibilitiesmightclosethe
gaptotheJVM.
WhenworkingwithmodernjavascriptMVCframeworkslikeBackbone.jsforHTML5front-
ends,moreandmorecodemovesfromtheserverback-endtothewebfront-end.This
approachcangreatlyincreasetheuserexperiencebecauseyousafealotofserver-
roundtripswhenusingbusinesslogicfromyourviews.
Backboneenablesyoutodefinemodelclasseswhichcanbeboundtoviews(e.g.HTML
forms).BackbonekeepstrackofupdatingthemodelwhentheuserinteractswiththeUIand
viceversa.Italsoaidsyoubysynchronizingyourmodelwiththeserver,e.g.bycallingthe
appropriatemethodofyourRESThandlerontheserverside.Soyouendupimplementing
businesslogicinyourfront-endcode,leavingyourservermodelresponsibleforpersisting
data.
ReusingbackbonemodelsontheserversideisquiteeasywithNashorn,asthefollowing
examplewilldemonstrate.Beforewestartmakesureyou'refamiliarwithwritingjavascript
fortheNashornEnginebyreadingmyNashornTutorial.
TheJavaModel
First,wedefineadomainclass Productinjavacode.ThisclassmightbeusedforCRUD
databaseoperations(savingtoandloadingfromadatasource).Keepinmindthatthisclass
isadumbJavaBeanwithoutanybusinesslogicapplied,becausewewantourfront-endto
becapableofexecutingthebusinesslogicrightfromtheUI.
classProduct{
Stringname;
doubleprice;
intstock;
doublevalueOfGoods;
}
TheBackboneModel
ModernJava-AGuidetoJava8
88UsingBackbone.jswithNashorn

Nowwedefinethebackbonemodelasthecounter-partofourjavabean.Thebackbone
model Productusesthesamedata-structureasthejavabean,sincethisisthedatawe
mightwanttopersistonthejavaserver.
Thebackbonemodelalsoimplementsthebusinesslogic:Themethod getValueOfGoods
calculatesthevalueofallproductsbymultiplying stockwith price.Eachtime stockor
pricechangestheproperty valueOfGoodsmustbere-calculated.
varProduct=Backbone.Model.extend({
defaults:{
name:'',
stock:0,
price:0.0,
valueOfGoods:0.0
},
initialize:function(){
this.on('change:stockchange:price',function(){
varstock=this.get('stock');
varprice=this.get('price');
varvalueOfGoods=this.getValueOfGoods(stock,price);
this.set('valueOfGoods',valueOfGoods);
});
},
getValueOfGoods:function(stock,price){
returnstock*price;
}
});
Sincethebackbonemodeldoesn'tuseanyNashornlanguageextensions,wecansafely
usethesamecodebothontheclient(Browser)andontheserver(Java)side.
KeepinmindthatIdeliberatelychoseareallysimplefunctionfordemonstratingpurposes
only.Realbusinesslogicshouldbemorecomplex.
Puttingbothtogether
Thenextgoalistore-usethebackbonemodelfromNashorn,e.g.onthejavaserver.We
wanttoachievethefollowingbehavior:bindallpropertiesfromthejavabeantothe
backbonemodel;calculatetheproperty valueOfGoods;passtheresultbacktojava.
First,wecreateanewscripttobeevaluatedsolelybyNashorn,sowecansafelyuse
Nashornextensionshere:
ModernJava-AGuidetoJava8
89UsingBackbone.jswithNashorn

load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js');
load('http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js');
load('product-backbone-model.js');
varcalculate=function(javaProduct){
varmodel=newProduct();
model.set('name',javaProduct.name);
model.set('price',javaProduct.price);
model.set('stock',javaProduct.stock);
returnmodel.attributes;
};
ThescriptfirstloadstherelevantexternalscriptsUnderscoreandBackbone(Underscoreis
apre-requirementforBackbone)andour Productbackbonemodelasdefinedabove.
Thefunction calculateacceptsa Productjavabean,bindsallpropertiestoanewly
createdbackbone Productandreturnsallattributesofthemodelbacktothecaller.By
settingtheproperties stockand priceonthebackbonemodel,property valueOfGoods
willautomaticallybecalculatedduetotheeventhandlerregisteredinthemodels
initializeconstructorfunction.
Finally,wecallthefunction calculatefromjava:
Productproduct=newProduct();
product.setName("Rubber");
product.setPrice(1.99);
product.setStock(1337);
ScriptObjectMirrorresult=(ScriptObjectMirror)
invocable.invokeFunction("calculate",product);
System.out.println(result.get("name")+":"+result.get("valueOfGoods"));
//Rubber:2660.63
Wecreateanew Productjavabeanandpassittothejavascriptfunction.Asaresultthe
method getValueOfGoodswillbetriggered,sowecanreadtheproperty valueOfGoodsfrom
thereturningobject.
Conclusion
ReusingexistingjavascriptlibrariesontheNashornEngineisquiteeasy.Backboneisgreat
forbuildingcomplexHTML5front-ends.InmyopinionNashornandtheJVMnowisagreat
alternativetoNode.js,sinceyoucanmakeuseofthewholeJavaeco-systeminyour
Nashorncodebase,suchasthewholeJDKAPIandallavailablelibrariesandtools.Keepin
mindthatyou'renottighttotheJavaLanguagewhenworkingwithNashorn-thinkScala,
Groovy,ClojureorevenpureJavascriptvia jjs.
TherunnablesourcecodefromthisarticleishostedonGitHub(seethisfile).Feelfreeto
forktherepositoryorsendmeyourfeedbackviaTwitter.
ModernJava-AGuidetoJava8
90UsingBackbone.jswithNashorn
