===== Java8 =====
New features:
* Methods as first class citizens
* Lambdas
* Passing code to methods (behavior parametrization)
* Default methods in Interfaces
* Streams
* Nashorn
* New Date API
* Optional
===== Glossar =====
|Predicate|boolean function, which takes some input|
|Functional interface| Interface to pass behavior |
===== Literature =====
|What's New in Java 8| https://leanpub.com/whatsnewinjava8/read |
===== New Concepts =====
|Behaviour parametrization |
|
===== Typing =====
Which type do new Java8 expressions have?
// Function. Gets Ranking. Returns Integer.
Function f = ranking -> ranking.value;
// Consumer - accepts sinle parameter. Returns nothing
Consumer c = Ranking:: echo;
// Predicate - boolean returning function
Predicate isLarger2 = ranking -> ranking.value > 2;
// MODELS
static class Ranking{
int value;
public static void echo(Object value){
System.out.println(String.format("Value is '%s'",value));
}
}
===== Behaviour parametrization =====
You can pass a method as a function-parameter.
For that the method-signature must define a **functional interface** as a paramter.
* functional Interfaces are located in package **java.util.function**
* e.g. Consumer
* Biconsumer ...
|Functional Interface|
An interface with **exactly one** single **abstract** method. \\
The interface may have many additional default methods. \\
Overriden abstract methods of Object.class - are not included in abstract interfacew, which count
[[http://www.tutorialspoint.com/java8/java8_functional_interfaces.htm|Some functional inerfaces]]
|
|
**Comparator is a functional interface**\\
Comparator is a functional interface, since it has two abstract methods:
* equals
* compare
and **equals** is a method from Object.class.
|
|
**Object is NOT a functional interface**\\
It does not have a single abstract method. \\
Lambdas can't be assigned to Object variables
Object o = () -> {return 2;} // invalid Object in not a functional interface
|
== @FunctionalInterface ==
Functional Interfaces may be annotated with **@FunctionalInterface** to clarify what they are!
== Exisiting generic functional interfaces ==
The generic functions have following names:
|..Consumer | provides a method with input. No output|
|..Supplier | provides output. No input.|
|..Function | provides method with input. And with output.|
|..Predicate| provides a boolean function|
|..Operation | method with input and output of same type|
== Behaviour parametrization examples ==
class Test{
public static void main(String[] args){
// passing myMethod, which may be used inside fun
Test.function(Test::myMethod);
}
// As defined by class FunctionalInterface - you can pass any function in here,
// which takes String as parameter AND
// which returns boolean
public void function(FunctionalInterface interface){
}
static interface FunctionalInterface{
boolean function(String parameter); // defines a single function operating on String, returning boolean
}
static class MyClass{
static boolean myMethod(String input){
return true;
}
}
===== Lambdas =====
==Glossar==
| Identifier |
Identifiers are used as names variables, functions. Identifiert
* starts with a **letter** or **_** or **$**
* is not a java-keyword (keywords are lister here: https://docs.oracle.com/cd/E19798-01/821-1841/bnbuk/index.html)
abcd123
|
| Literals |
Notations for **constant values**, **build in types**
123.33
|
| Operators |
* Mathematical operators,
* call operators ()
* array operator []
* increment operator ++
The full operator list is here: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/opsummary.html
**Not** operators:
print
return
|
| Expression |
It contains:
* identifiers,
* literals,
* operators
Expressions:
"String is an expression too"
"Alan" + i
System.out.print("ho")
|
| Statement |
* Statements are made up of expressions.
* Form a complete unit of execution
* End with **;**
More here: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html
Statements:
System.out.print("ho");
return "Alan" + i;
|
==Lamda Syntax==
1. (parameters) -> expression
2. (parameters) -> { statements; }
Lamda Examples:
() -> {} // valid 1
() -> "Raoul" // valid 1
() -> {return "Mario";} // valid 1
(Integer i) -> return "Alan" + i; // invalid. Valid version: (Integer i) -> {return "Alan" + i;}
(String s) -> {"Iron Man";} // invalid. Valid version: (String s) -> {return "Iron Man";}
More valid examples:
(TYPE name) -> FUNCTION(name)
(String s) -> s.toUpperCase() + " !!!"
(Apple a) -> a.getWeigth() > 150
== Type inference ==
Compiler can deduce the input type from the functional interface. \\
So you can omit the type in lambdas.
legal usages of lamdas: \\
List greenApples = filter(inventory, a -> "green".equals(a.getColor())); // a has no type. It becomes an Apple automagically
==Method reference==
Methods can be references, to use them as lambdas:
Integer::parseInt // local method on class
Apple::getWeight // static method on class
new Apple()::getWeight // on instance
==Constructor reference==
Supplier c1 = Apple::new; // supplier takes no pramas, creates Apple. COnstructor is a supplier
Apple a1 = c1.get(); // produce an apple by calling a suplier
===== Streams =====
== Summary ==
Streams introduce **internal collection navigation** where you don't need to. \\
As a contra to extrnal navigation, where you explicitely iterate a collection.
Streams describe **computations of Data sets**, \\
whereas collections describe the storage and access.
Operations on Streams are **parallelized** automagically, when using parallelStream()
inventory.stream().filter... // not parallelized
inventory.parallelStream().filter... // parallelized
== Methods ==
The stream methods are separated into:
|intermediate|
returns a stream. Which allows to execute the next operation, as defined in chain pattern.
.stream().filter(...).distinct().limit(3)...
|
|terminal |
operation which returns a non string. FInalizes the chain of stream commands.
.stream()...count();
.stream()...collect(toList());
.stream()....forEach(System.out::println);
|
**Intermesiate methods**
^ method ^ argument ^ result ^ mapping ^
|filter| T-> boolean| Stream |
|map| T -> R| Stream |
|limit| int | Stream |
|skip| int | Stream |
|sorted| T,T - int| Stream |
|distinct| | Stream |
|flatMap| | | Stream -> Stream |
**terminal methods**
^ method ^ argument ^ result ^
|forEach| | |
|count| | int |
|collect| | |
|allMatch| | boolean |
|anyMatch| | boolean |
|noneMatch| | boolean |
|findFirst| | Optional |
|findAny| | Optional |
|reduce|T, T -> T | Optional |
== single pass ==
All the stream-methods chained one after another are executed in a single, internal iteration.
When you apply a sequence of operations to a stream, these are merely saved up.
Only when you apply a terminal operation to a stream is anything actually computed. This has the great advantage when you apply several
operations (perhaps a filter and a map followed by a terminal operation reduce) to a stream, then the stream has to be traversed only once instead of for each operation.
THe following code would produce the floowing output:
**code**
dishes.stream().filter(a -> {
System.out.println(a.name);
return true;
}).distinct().map(a -> {
System.out.println(a.type);
return a.name;
});
**output**
pork // name
flesh // type
pumkin // name
vegetables // type
**Name** and **type** come **one after another** or each element.
This means that steps during parallel execution - methods that can block the parallel execution should not be executed inside the Stream-pipeline.
Assume you have two versions of getting prices from futures:
// directly chaining map methods to create Future, get Result is wrong. Because f.get() will be called after every future creation and block further Future creation (Single Pass)
stream.map(Helper::createFuture).map((Future f) -> g.get()).asList()
// collecting futures into collection first is the right parallel way. When we do the first "get" all Futures already exist and computing
List futures = stream.map(Helper::toFuture).asList();
List prices = futures.asStream().map((Future f) -> g.get())
On the Image the upper Situation is depicted
{{http://i520.photobucket.com/albums/w327/schajtan/2016-01-25_14-33-18_zpsgm9w0fjz.png}}
== Filling the Stream ==
// filling the stream with content using "iterate"
Stream.iterate(0l, i -> i + 1)
.limit(100);
== Collect and Partition methods - the better reduce ==
There are plently of collectors, which are availalbe via **java.util.stream.Collectors**.
Just import all static collector methods and vars to use them by name.
import static java.util.stream.Collectors.*;
**Glossar**
|multilevel reduction| groupingBy collector creates substreams, for different grouping criteria. Substreams may be transfered/grouped by another collector, called a downstream collector |
|downstream collector| collector, to transfer substreams (substreams created by groupingBy collector)|
| reduce-accumulator | method B, A -> B , which converts an STREAM-ENTRY of type A to B. It is applied on EVERY Stream Element. It is passed the accumulated reduce value B so far and the next stream value A. It returns the value, resulting from accumulation of the next stream-element A |
| reduce-combiner | when stream is reduced in a parallel way - combiner will combine every result, accumulated on different Streams. |
^ Method ^ Returns ^ Describtion ^ Example ^
| toList | List | Gather all the stream’s items in a List. | List dishes = menuStream.collect(toList());
|
| toSet | Set | Gather all the stream’s items in a Set, eliminating duplicates. | Set dishes = menuStream.collect(toSet());
|
| toCollection | Collection | Gather all the stream’s items in the collection created by the provided supplier. | Collection dishes = menuStream.collect(toCollection(), ArrayList::new)
|
| counting | Long | | long howManyDishes = menuStream.collect(counting());
|
| summingInt | Integer | | int totalCalories = menuStream.collect(summingInt(Dish::getCalories));
|
| averagingInt | Double | | double avgCalories = menuStream.collect(averagingInt(Dish::getCalories));
|
| summarizingInt | IntSummary-Statistics | Collect statistics regarding an Integer property of the items in the stream, such as the maximum, minimum, total, and average. | IntSummaryStatistics menuStatistics = menuStream.collect(summarizingInt(Dish::getCalories));
|
| joining | String | | String shortMenu = menuStream.map(Dish::getName).collect(joining(", "));
|
| maxBy / minBy | | | Optional lightest = menuStream.collect(minBy(comparingInt(Dish::getCalories)));
|
| reducing | The type produced by the reduction operation | Reduce the stream to a single value starting from an initial value used as accumulator and iteratively combining it with each item of the stream using a BinaryOperator. | int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
|
| collectingAndThen | The type returned by the transforming function | | int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size));
|
| groupingBy | Map> | Maps the stream to groups (as hashmap keys) | Map> dishesByType = menuStream.collect(groupingBy(Dish::getType)); |
| partitioningBy | Map> | | Map> vegetarianDishes =menuStream.collect(partitioningBy(Dish::isVegetarian));
|
== Collector Interface - implementing own collectors ==
/* COLLECTOR
*
* Collector
*
* T - Type of input item in stream
* A - intermediate Type of Objects, during calculation
* R - Result Type
*
* e.g. Collector which collects Stream of Strings to List
* T - String
* A - List
* R - List
*
* Supplier supplier() - provides initial value of intermediate Type
* BiConsumer accumulator() - input -> the A value calculated so far. T is the next value in stream
* Function finisher() - function is executed after iteration over all elements is done
* BinaryOperator combiner() - function which combined two intermediate Objects. This one makes multithreading possible
* Set characteristics - some optimization hints. UNORDERED, CONCURRENT, IDENTITY_FINISH
*
*
* supplier - ArrayList::new
* accumulator - (List a, String b) -> {a.add(b)}
* finisher - Functions::identity
* combiner - (List a, List b) -> {a.addAll(b)}
* characteristics - return Collections.unmodifyableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT ))
*/
== Parallelism ==
Boxed Values are a performace killer.
Using a Stream with primitives - improves performance by factor x6
long max = 90000000;
// LongStream uses primitives (long)
start = System.currentTimeMillis();
summ = LongStream.iterate(0l, i -> i + 1)
.limit(max)
.reduce(0l, Long::sum);
System.out.println("Sequential LongStream Ready filling after: "+ (System.currentTimeMillis()-start) +"ms");
// 220ms
// Stream generates Boxed Values (Long)
start = System.currentTimeMillis();
summ = Stream.iterate(0l, i -> i + 1)
.limit(max)
.reduce(0l, Long::sum);
System.out.println("Sequential Stream Ready filling after: "+ (System.currentTimeMillis()-start) +"ms");
// 1228 ms
Reducing DIshes to Strings in a parallel way:
// parallel streams of Dishes, reduced to String, which is a concatenated list of names
String res = getExampleStream().parallel().reduce("",
new BiFunction() {
@Override
public String apply(String t, Dish u) {
return t+u.name;
} //accumulator - can convert the Stream content to something else (String)
},
(a,b) -> {
return a+b;
} // combiner - used in the case of using parallel streams
);
===== Default methods in Interfaces =====
It is possible to implement default methods in interfaces. \\
So multiple inheritance is some sort of allowed now in java!!!
The default interface methods are marked by keyword **default**
default void methodName(){...
public class Pazak implements HasDefaultMethodKu, HasDefaultMethodKuToo{
// have to override method #ku() to explicetly say which interface's method to execute
@Override
public void ku() {
HasDefaultMethodKu.super.ku();
}
}
public interface HasDefaultMethodKu{
default void ku(){
System.out.println("Say Ku!");
}
}
public interface HasDefaultMethodKuToo{
default void ku(){
System.out.println("Say Ku Too!");
}
}
===== Optional class =====
Return that instead of **null** to avoid NPE
* explicitely define where the var may be null
**Methods**
|isPresent| |
|ifPresent(Consumer block)| |
|get| value or NoSuchMethod-Exception |
|T orElse(T other)| |
public class Optionals {
String name = "Сапожник козоед";
public static void main(String[] args) {
Optionals optionals = new Optionals();
System.out.println(optionals.getName().get());
// add defaults
System.out.println(optionals.getName().orElse("defaultName"));
System.out.println(optionals.getEmptyName().orElse("defaultName"));
}
Optional getName(){
return Optional.of(name);
}
Optional getEmptyName(){
// explicitly say that this var is nullable
return Optional.ofNullable(null);
// return Optional.of(null); // would produce a NullPointerException
}
}
===== CompletableFuture class =====
Like a future with more functional-style interface.
CompletableFuture f = CompletableFuture.supplyAsync(() -> calculatePrice());
== occured exceptions rethrown on get()==
The occured Exceptions are rethrown on **get()**
CompletableFuture f = ...
try {
double calculatedPrice = f.get();
System.out.println(String.format("Everything was fine. Price calculated: "+calculatedPrice));
return calculatedPrice;
} catch (Exception e1) {
System.out.println(String.format("I know - an Exception occured earlier on another thread: %s.", e1.getCause()));
return (double) 0;
}
== Chainable ==
The CompletableFuture can be chained,
to execute some code when the future has completed the computation.
CompletableFuture c = null;
CompletableFuture c2 = null;
CompletableFuture cc = c.thenApply((Integer i) -> "Done");
/* thenApply((FutureType) -> T) -> CompletableFuture
* Executed, when previous Future is done.
* Modifies the value "INSIDE" the completableFuture container to T */
/* thenRun(() -> Void) -> CompletableFuture
* Executes some code (no params are passed), when previous Future is done */
/* thenAccept(FutureType -> Void) -> CompletableFuture
* Executed, when the previous Future is done
* VOID will be inside the Future after this call. To preserve some Type T in FUture - use thenApply()
*/
CompletableFuture ccc = c.thenCompose((Integer inte) -> CompletableFuture.runAsync(() -> System.out.println("Done")));
/* thenCompose(FutureType) -> CompletableFuture) -> CompletableFuture
* Executes a SECOND CompletableFuture, when the first is done */
// thenCombine
CompletableFuture aaa = c.thenCombine(c2, (Integer ii, String ss) -> 2.2);
/* thenCombine(CompletableFuture, Function(FutureType1, FutureType2) -> T) -> CompletableFuture
* Executes FIRST and SECOND ComputableFutures. Produces the results in a Function which itselfe produces a ComputableFunction
*/
Chaining can be used in streams, to implement real parallel (not only concurrent) evaluation.
// List -> List>
(List l) -> {
List> listFuturesFIlledPrice = l
.stream()
// Picture -> Futures
// echo result of future when ready
.map((Picture picturePriceless) -> {
CompletableFuture picFutureWithPrice;
// ASYNC DELAYED: get price
picFutureWithPrice = CompletableFutures.getFutureFillingPriceInPictureDelayed(picturePriceless);
// react immediately after calculation is done
picFutureWithPrice = picFutureWithPrice.thenApply(CompletableFutures::echoPictureWhenFutureReady);
return picFutureWithPrice;
}
)
.collect(Collectors.toList());
return listFuturesFIlledPrice;
}
===== Date API =====
Basic classes
|Instant|
// INSTANT - to be used by machines
Instant instant = Instant.ofEpochSecond(1454523011);
// nanosecond adjustment - 1sec=1.000.000.000nanosec
Instant.ofEpochSecond(0); // 1.January.1970 UTC
Instant.ofEpochSecond(2, 1000_000_000); // 2sec + 1sec
Instant.ofEpochSecond(3, -1000_000_000); // 4sec - 1sec
// Instant -> LocalDate, LocalTime
LocalDate dateOfInstant = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
LocalTime timeOfInstant = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalTime();
|
|LocalDate|
LocalDate localDateNow = LocalDate.now();
LocalDate localDateNowLondon = LocalDate.now(ZoneId.of("UTC+0"));
LocalDate localDate = LocalDate.of(2016, 2, 16);
int fieldYear = localDate.get(ChronoField.YEAR);
|
|LocalTime|
LocalTime time = LocalTime.of(14, 55, 54, 11 ); // hour, minute, second, milli
LocalTime time2 = LocalTime.of(14, 55, 54 ); // hour, minute, second
LocalTime time3 = LocalTime.of(14, 55 ); // hour, minute
int hourTime = time.getHour();
int minuteTime = time.getMinute();
|
|LocalDateTime|
// 1415-07-6T6:55:20
LocalDate dateOne = LocalDate.of(1415, Month.JULY, 6);
LocalTime timeOne = LocalTime.of(6, 55, 20);
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(dateOne, timeOne);
LocalDateTime dt3 = dateOne.atTime(13, 45, 20);
LocalDateTime dt4 = dateOne.atTime(timeOne);
LocalDateTime dt5 = timeOne.atDate(dateOne);
|
|ZoneId|
ZoneId romeZone = ZoneId.of("Europe/Rome");
ZoneId berlinZone = ZoneId.of("Europe/Berlin");
ZoneId myZone = TimeZone.getDefault().toZoneId();
|
|ZonedDateTime|
ZoneId berlinZone = ZoneId.of("Europe/Berlin");
ZonedDateTime zonedDateTimeBerlin1 = dateOne.atStartOfDay(berlinZone);
ZonedDateTime zonedDateTimeBerlin2 = localDateTimeOfGuss.atZone(berlinZone);
|
|DateTimeFormatter|
LocalDate parsedDate;
parsedDate = LocalDate.parse("2014-03-18");
parsedDate = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_DATE);
parsedDate = LocalDate.parse("18/03/2014", DateTimeFormatter.ofPattern("dd/MM/yyyy"));
parsedDate = LocalDate.parse("18.03.2014", DateTimeFormatter.ofPattern("dd.MM.yyyy"));
parsedDate = LocalDate.parse("18 März 2014", DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.GERMAN));
DateTimeFormatter germanFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH) // 6
.appendLiteral(".")
.appendValue(ChronoField.MONTH_OF_YEAR) // 7
.appendLiteral("( alias ")
.appendText(ChronoField.MONTH_OF_YEAR) // July
.appendLiteral(")")
.appendLiteral(".")
.appendValue(ChronoField.YEAR) // 1941
.parseCaseInsensitive()
.toFormatter(Locale.GERMAN);
System.out.println(germanFormatter.format(dateOne));
||
|Duration|
/*
* Duration is CRAP, because based on Seconds / Nanos.
* - you only can compute a duration on "time based units" - something what can handle seconds like LocalTime.
* The compiler still allows computing Duration between LocalDate, or getting YEARS from Duration,
* which results in an Exception.
* WHY OR WHY CAN'T I JUST CONVERT SECONDS IN ANY UNIT WITHOUT EXCEPTIONS, AS EXPECTED?!
*/
// Duration only works with Seconds-supporting types. Using LocaDate - throws an exception
// Duration durationSinceGuss = Duration.between(dateOne, LocalDate.now());
Duration durationSinceGuss1 = Duration.between(LocalDateTime.of(dateOne, LocalTime.MIDNIGHT),LocalDateTime.now());
Duration durationSinceGuss2 = Duration.between(timeOne,LocalTime.now());
// Duration durationSinceGuss3 = Duration.between(dateOne,LocalDate.now()); // exception. It trys to convert LocalDate to Seconds, which is not allowed
|
|Period|
Period periodBetweenGuss = Period.between(dateOne, LocalDate.now());
long yearsSinceGuss7 = periodBetweenGuss.getYears();
|
|TemporalAdjuster| LocalDate dateNextSunay = dateOne.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); |
The Time Units are contained by **ChronoUnit.class** and separated in timeBased and dateBased:
|
It is FUCKING UNCLEAR, why DAYS ARE NOT TIMEBASED?!
Still it is important,
some operations are only legal for time- or dateBased units:
like getting the amount of years from a Duration.
|
^TimeBased ^DateBased^
|Nanos | Days|
|Micros | Weeks |
|Millis | Months |
|Seconds | Years |
|Minutes | Decades |
|Hours | Centuries |
|HalfDays | Millennia |
| | Eras |
The same separation exists with Classes:
^TimeBased ^DateBased^
|LocalDateTime| LocalDateTime|
|LocalDate| LocalTime|
|Period| Duration|
mixing them up produces an **UnsupportedTemporalTypeException**.
localDate.plus(1, CHronoUnit.SECOND) // UnsupportedTemporalTypeException
localTime.plus(1, CHronoUnit.DAY) // UnsupportedTemporalTypeException
// LocalDateTime supports both
localDateTime.plus(1, ChronoUnit.SECONDS);
localDateTime.plus(1, ChronoUnit.DAYS);
CHronofield - contains diffrent kinds of data info names, which may be requested from diffrent TemporalFields.
int fieldYear = localDate.get(ChronoField.YEAR);
/*
ChronoField.
NanoOfSecond
NanoOfDay
MicroOfSecond
MicroOfDay
MilliOfSecond
MilliOfDay
SecondOfMinute
SecondOfDay
MinuteOfHour
MinuteOfDay
HourOfAmPm
ClockHourOfAmPm
HourOfDay
ClockHourOfDay
AmPmOfDay
DayOfWeek
AlignedDayOfWeekInMonth
AlignedDayOfWeekInYear
DayOfMonth
DayOfYear
EpochDay
AlignedWeekOfMonth
AlignedWeekOfYear
MonthOfYear
ProlepticMonth
YearOfEra
Year
Era
InstantSeconds
OffsetSeconds
*/
===== Code - Java8 Style =====
How to write code in Java8?
== Lambdas instead of anonymous Classes==
You can replace many anonymous classes with method calls.
Important:
^ theme ^ anonymous class ^ lambdas ^
| this | means the anonymous class | means the wrapping class |
| overriding vars| is allowed | is forbidden |
| typing | is unambigous | may become ambigous - needs explicit typing |
Runnable runnable = new Runnable(){
void execute(){
System.out.println("Go!")
}
}
Runnable runnable = () -> System.out.println("Go!"); // ok
int mVar = 20;
Runnable runnable = () -> {
int mVar = 30; // not ok - overriding vars not allowed
System.out.println("Go!");
}
public static void doSomething(Runnable r){ r.run(); }
public static void doSomething(Task a){ r.execute(); }
doSomething(() -> System.out.println("Go!")); // not ok - call doSOmething for Task or Runnable?
doSomething((Task)() -> System.out.println("Go!")); // ok
== Method Reference with helper static methods ==
Stream stream = ...
stream.map(String::length);
stream.map(Helpers::quote);
String quote(String str){
return String.format("'%s'",str);
}
== Use native collectors ==
Native collectors are known by others. Are better readable.
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
== Replace loops by streams API ==
Better readability, optimization behind the scenes.
// collecting names of dishes, which have > 300 calories
List dishNames = new ArrayList<>();
for (Dish dish : menu) {
if (dish.getCalories() > 300) {
dishNames.add(dish.getName());
}
}
// intent is better readable.
menu.parallelStream()
.filter(d -> d.getCalories() > 300)
.map(Dish::getName)
.collect(toList());
== Deferred conditional execution ==
// message is evaluated EVERY TIME, even if the level does not fit
logger.log(Level.FINER, "Problem: " + generateDiagnostic());
// to avoid - pass a lambda, which will be called ONLY IF LEVEL FITS
logger.log(Level.FINER, () -> "Problem: " + generateDiagnostic());
== Execute around ==
// same code, which is executed around some variable logic
void method1(){
doPreparation();
//variable method here
varMethod2();
cleanUp();
}
void method2(){
doPreparation();
//variable method here
varMethod2();
cleanUp();
}
// better
void method(Runnable r){
doPreparation();
r.execute();
cleanUp();
}
method(()->varMethod1());
method(()->varMethod2());
===== Functional programming style =====
With Java8 the programming style is pushing towards the functional edge. \\
Some rules for writing functional code:
=== Functions must have ===
Requirenments to functions
== have no side Effects ==
Functions may only change private variables.
== have referential transparency ==
Calling same function always returns the same result. \\
E.g. readNextLine() is NOT referential transparent. It alwas returns another line.
=== Do ===
What to do, when programming:
== get Function, return Function ==
pass functions to methods. Return new functions with functionality around it.
Function transformationPipeline
= addHeader.andThen(Letter::checkSpelling)
.andThen(Letter::addFooter);
== use Carriying ==
create FUnctions producing FUnctions, \\
which will prefill some constants.
// carrying
Function createConvertingFunction(double course){
return (double x) -> course * x;
}
Function eurToChf = createConvertingFunction(0.9);
Function bintcoinToEur = createConvertingFunction(421);
When function reuses another function and \\
when preselecting some arguments (but not all) have been passed - \\
then **the function is partially applied**
Function echoColoredObject(Object object, String color){
(object, color) -> System.out.println("The "+object+" has the color "+color);
}
// partially apply function echoColoredObject
Function echoColoredBall(String color){
(color) -> echoColoredObject("Ball", color)
}
===== Interfaces relevant for Lambdas =====
If you would like to create a method accepting a lamda use the Argument, typed with one of those methods:
**java.util.function.IntBinaryOperator.**
Examples:
Consumer c = aDouble -> System.out.println(aDouble); // no output
Function f = aDouble -> String.valueOf(f); // function returns what you define
DoubleFunction d = doubleValue -> String.valueOf(doubleValue); // same as previous
IntBinaryOperator plusOperation = (a, b) -> a + b; // operator on two int numbers a and b
LongPredicate l = longValue -> longValue < 0l; // boolean function
===== Terminal Methods =====
== findAny / findFirst ==
List list = List.of(1, 2, 3, 4, 9, 8, 7, 6, 1, 2, 3);
// gives maybe 7, 8 or 9, cause findAny doesnt respect order
int res = list
.stream()
.parallel()
.filter(integer -> integer > 6)
.findAny()
.orElseThrow();
assertThat(res, anyOf(is(7), is(8), is(9)));
// gives 9, cause its the first in the row match to filter
int res2 = list
.stream()
.parallel()
.filter(integer -> integer > 6)
.findFirst()
.orElseThrow();
assertThat(res, is(9));
== reduce ==
// cruel method to concat numbers, just to demonstrate reduce
List list = List.of(1, 2, 3, 4, 9, 8, 7, 6, 1, 2);
Integer res = list
.stream()
.mapToInt(value -> value)
.reduce(0,
(integer, integer2) -> {
return Integer.parseInt(String.format("%s%s", integer, integer2));
}
);
System.out.println(res); // 1234987612
// better way would be with java8
String res2 = list
.stream()
.map(String::valueOf)
.collect(Collectors.joining());
System.out.println(res2); // 1234987612