===== 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