Kamil Lolo
Kamil Lolo
https://klolo.github.io
programista
javascript
scrum master
technical leader
trener
devops
administrator
Java
javascript
kung fu
https://kariera.comarch.pl/oferty-pracy/
Lambda
klasa anonimowa
byte code
clousure
asembler
serializacja
JIT
historia lambdy
Java 10/11
invoke dynamic
functional interface
type erasure
method reference
stream
debug
effective final
clean code
lambda
?
In computer programming, an anonymous function (function literal, lambda abstraction, or lambda expression) is a function definition that is not bound to an identifier.
Paulo Lambdelho
// 1
function getValue(x) { return x * x; }
alert(getValue(10));
// 2
alert(function(x) { return x * x; });
// 3
alert((x => x*x)(10));
(define (adder n) (lambda (x) (+ x n)))
public static void main(String... args) {
List<String> names = Arrays.asList("Mateusz", "Zosia", "Ania");
names.sort(new Comparator<String>() {
@Override
public int compare(final String s1, final String s2) {
return s1.compareTo(s2);
}
});
}
public interface Comparator<T> {
int compare(T o1, T o2);
}
public static void main(String... args) {
List<String> names = Arrays.asList("Mateusz", "Zosia", "Ania");
names.sort(new Comparator<String>() {
@Override
public int compare(final String s1, final String s2) {
return s1.compareTo(s2);
}
});
}
lista zawiera obiekty typu string
i tylko takie mogę tutaj sortować
dostaje dwa parametry typu T
zwracam int
jedyna ważna linijka
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(final String s1, final String s2) {
return s1.compareTo(s2);
}
};
Comparator<String> comparator = (s1, s2) { return s1.compareTo(s2); }
Comparator<String> comparator = (s1, s2) -> s1.compareTo(s2);
names.sort((s1, s2) -> { return s1.compareTo(s2); });
names.sort((s1, s2) -> s1.compareTo(s2));
new Thread(() -> System.out.println("hello from another thread")).start();
foo( param -> System.out.print(param));
Runnable r = () -> System.out.println();
// Lambda to tylko implementacja interfejsu z jedna metodą abstrakcyjna
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "some string";
}
};
Callable<String> callable = () -> "some string";
Co znajduje się w pierwszych bajtach każdego pliku class?
Class files are identified by the following 4 byte header (in hexadecimal): CA FE BA BE.
public static void main(String[] args) {
String result = "";
for (int i = 0; i < 1_000_000; ++i) {
result += i;
}
System.out.println(result);
}
public static void main(String[] args) {
String json = "{";
json += "a:'someValue'";
}
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String a:'someValue'
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
An implementation (of compiler) may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
0: ldc #2 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: ldc #3 // int 1000000
8: if_icmpge 36
11: new #4 // class java/lang/StringBuilder
14: dup
15: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_1
30: iinc 2, 1
33: goto 5
36: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
39: aload_1
40: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
43: return
public static void main(String[] args) {
String result = "";
for (int i = 0; i < 1_000_000; ++i) {
result += i;
}
System.out.println(result);
}
public Main();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LMain;
public static void main(java.lang.String...);
flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
Code:
stack=4, locals=2, args_size=1
0: iconst_3
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String Mateusz
8: aastore
...
19: invokestatic #6 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
22: astore_1
23: aload_1
24: new #7 // class Main$1
27: dup
28: invokespecial #8 // Method Main$1."<init>":()V
31: invokeinterface #9, 2 // InterfaceMethod java/util/List.sort:(Ljava/util/Comparator;)V
36: return
LocalVariableTable:
Start Length Slot Name Signature
0 37 0 args [Ljava/lang/String;
23 14 1 names Ljava/util/List;
List<String> names = Arrays.asList("Mateusz", "Zosia", "Ania");
names.sort(new Comparator<String>() {
public int compare(final String s1, final String s2) {
return s1.compareTo(s2);
}});
Main$1();
// analogiczny pusty konstruktor jak wcześniej
public int compare(java.lang.String, java.lang.String);
descriptor: (Ljava/lang/String;Ljava/lang/String;)I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: aload_1
1: aload_2
2: invokevirtual #2 // Method java/lang/String.compareTo:(Ljava/lang/String;)I
5: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this LMain$1;
0 6 1 s1 Ljava/lang/String;
0 6 2 s2 Ljava/lang/String;
public int compare(java.lang.Object, java.lang.Object);
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: aload_1
2: checkcast #3 // class java/lang/String (checks objectref is of a certain type)
5: aload_2
6: checkcast #3 // class java/lang/String
9: invokevirtual #4 // Method compare:(Ljava/lang/String;Ljava/lang/String;)I
12: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 this LMain$1;
invokevirtual
invokeinterface
invokespecial
invokestatic
public static void main(java.lang.String...);
flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
Code:
stack=4, locals=2, args_size=1
// tworzenie stack frame, tablicy etc.
19: invokestatic #6 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
22: astore_1
23: aload_1
24: invokedynamic #7, 0 // InvokeDynamic #0:compare:()Ljava/util/Comparator;
29: invokeinterface #8, 2 // InterfaceMethod java/util/List.sort:(Ljava/util/Comparator;)V
34: return
LocalVariableTable:
Start Length Slot Name Signature
0 35 0 args [Ljava/lang/String;
23 12 1 names Ljava/util/List;
void printElements(java.util.List<java.lang.String>);
Code:
0: aload_1
1: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
4: dup // *duplicate the value on top of the stack
5: invokevirtual #3 // Method java/lang/Object.getClass:()Ljava/lang/Class;
8: pop // *discard the top value on the stack
9: invokedynamic #4, 0 // InvokeDynamic #0:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
14: invokeinterface #5, 2 // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
19: return
// 1
void printElements(List<String> in) {
in.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
}
// 2
void printElements(List<String> in) {
in.forEach(it -> System.out.println(it));
}
// access flags 0x0
// signature (Ljava/util/List<Ljava/lang/String;>;)V
// declaration: void printElements(java.util.List<java.lang.String>)
printElements(Ljava/util/List;)V
L0
LINENUMBER 6 L0
ALOAD 1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
DUP
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
POP
INVOKEDYNAMIC accept(Ljava/io/PrintStream;)Ljava/util/function/Consumer; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory(
Ljava/lang/invoke/MethodHandles$Lookup;
Ljava/lang/String;Ljava/lang/invoke/MethodType;
Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;
Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)V,
// handle kind 0x5 : INVOKEVIRTUAL
java/io/PrintStream.println(Ljava/lang/String;)V,
(Ljava/lang/String;)V
]
INVOKEINTERFACE java/util/List.forEach (Ljava/util/function/Consumer;)V
L1
LINENUMBER 7 L1
RETURN
L2
LOCALVARIABLE this LGeneric; L0 L2 0
LOCALVARIABLE strings Ljava/util/List; L0 L2 1
// signature Ljava/util/List<Ljava/lang/String;>;
// declaration: java.util.List<java.lang.String>
MAXSTACK = 3
MAXLOCALS = 2
InnerClasses:
public static final #68= #67 of #71;
//Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #35 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;
Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;) Ljava/lang/invoke/CallSite;
Method arguments:
#36 (Ljava/lang/Object;)V
#37 invokestatic Main.lambda$main$0:(Ljava/lang/String;)V
#38 (Ljava/lang/String;)V
// CallSite - holder for a variable
private static CallSite bootstrapLambda(Lookup lookup, String name, MethodType type){ //
// lookup = provided by VM - factory for creating method handles
// name = "lambda$printElements$0", provided by VM
// type = String -> void
MethodHandle lambdaImplementation = lookup.findStatic(lookup.lookupClass(), name, type);
return LambdaMetafactory.metafactory(lookup, "accept",
//signature of lambda factory
MethodType.methodType(Consumer.class),
//signature of method Consumer.accept after type erasure
MethodType.methodType(void.class, Object.class),
//reference to method with lambda body
lambdaImplementation,
type);
}
void printElements(List<String> strings){
Consumer<String> lambda = invokedynamic(#bootstrapLambda)
strings.forEach(lambda);
}
pseudo kod!
private static synthetic lambda$printElements$0(Ljava/lang/String;)V
L0
LINENUMBER 7 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 0
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
RETURN
L1
LOCALVARIABLE it Ljava/lang/String; L0 L1 0
MAXSTACK = 2
MAXLOCALS = 1
}
// generated by Java compiler
private static void lambda$printElements$0(String item){
System.out.println(item);
}
class A {
public void foo() {
List<String> list = ...
list.forEach(s -> { System.out.println(s); });
}
}
class A {
public void foo() {
List<String> list = ...
list.forEach( [lambda for lambda$1 as Block] );
}
static void lambda$1(String s) {
System.out.println(s);
}
}
stateless lambda
class A {
public void foo() {
List<String> list = ...
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
}
);
}
}
class B {
private int top = ...
public void foo() {
List<Person> list = ...
final int bottom = ...
list.removeIf( p -> (p.size >= bottom && p.size <= this.top) );
}
}
class B {
private int top = ...
public void foo() {
List<Person> list = ...
final int bottom = ...
list.removeIf( [ lambda for lambda$1 as Predicate capturing (bottom, top) ]);
}
boolean lambda$1(int bottom, Person p) {
return p.size >= bottom && p.size <= this.top;
}
}
One key difference between using Anonymous class and Lambda expression is the use of this keyword. For anonymous class ‘this’ keyword resolves to anonymous class, whereas for lambda expression ‘this’ keyword resolves to enclosing class where lambda is written.
- Viral Patel
public class Demo {
...
@Override
public String toString() {
return "demo";
}
}
public static void main(final String... args) {
final Demo demo = new Demo();
}
Demo() {
// 1. com.comarch.training.Demo$1@69991479
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(this);
}
})
.start();
// 2. demo
new Thread(() -> System.out.println(this)).start();
}
There are a number of ways we might represent a lambda expression in bytecode, such as inner classes, method handles, dynamic proxies, and others. Each of these approaches has pros and cons. In selecting a strategy, there are two competing goals: maximizing flexibility for future optimization by not committing to a specific strategy, vs providing stability in the classfile representation.
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html
static String lastName = "";
public static void main(String... args) throws InterruptedException {
String userName = "";
Runnable r1 = () -> lastName = "Henio";
// ERROR: local variables referenced from a lambda
// expression must be final or effectively final
Runnable r2 = () -> userName = "Henio";
new Thread(r).start();
Thread.sleep(100);
System.out.println(lastName);
}
A functional interface is an interface that has just one abstract method (aside from the methods of Object), and thus represents a single function contract.
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
@FunctionalInterface
interface SomeInterface {
void foo();
default void bu() {}
static void staticBu() {}
}
interface A {
void foo();
void bu();
}
@FunctionalInterface
interface B extends A {
default void bu(){}
}
interface X { Iterable foo(Iterable<String> arg); }
interface Y { Iterable foo(Iterable arg); }
interface Z extends X, Y {}
@FunctionalInterface
interface MyFI {
// ERROR: Multiple non-overriding abstract methods found in interface
void method1();
void method2();
}
@FunctionalInterface
interface NonFunc {
boolean equals(Object obj);
}
interface X { int m(Iterable<String> arg); }
interface Y { int m(Iterable<Integer> arg); }
interface Z extends X, Y {}
@FunctionalInterface
interface NonFunc { }
interface X { int m(Iterable<String> arg, Class c); }
interface Y { int m(Iterable arg, Class<?> c); }
interface Z extends X, Y {}
static abstract class F {
public abstract void foo();
}
public static void main(String... args) {
//Error: Target type of lambda conversion must be an interface
F f = () -> {};
}
http://mail.openjdk.java.net/pipermail/lambda-dev/2013-March/008441.html
private static interface IMagicAlgorithm {
long calculate(long x);
}
private static long calculate(long x) {
return x * x;
}
public static void main(String... args) throws InterruptedException {
// obliczenia zawarte w lambda - slabe do utrzymania
IMagicAlgorithm algorithm1 = x -> x * x;
// wywolanie istenijacej metody - nadal nadmiarowy kod
IMagicAlgorithm algorithm2 = x -> calculate(x);
// referencja do metody!
IMagicAlgorithm algorithm3 = Demo::calculate;
}
-lambda tworzy anonimową metodę, co w przypadku kiedy mam już metodę i chcemy ją wykorzystać?
-wisienka na torcie lambd
private static String identity(String param) {
return param;
}
public static void main(String... args) {
Function<String, String> fun = Main::identity;
System.out.println(fun.apply("Hello"));
}
stack=3, locals=2, args_size=1
0: invokedynamic #2, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
5: astore_1
6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
9: aload_1
10: ldc #4 // String Hello
12: invokeinterface #5, 2 // InterfaceMethod java/util/function/Function.apply:...
17: checkcast #6 // class java/lang/String
20: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
public void foo() {
final char[] numbers = {'A', 'b', 'C'};
final String result = Stream.of(numbers)
.map(String::valueOf) // metoda statyczna
.map(String::toLowerCase) // metoda instancji ( s-> s.toLowerCase() )
.map(this::appendeSeparator) // metoda obiektu
.map(WorkshopTest::trimmer) // metoda obiektu
.collect(Collectors.joining());
System.out.println(result);
}
private static String trimmer(final String input) {
return input.trim();
}
private String appendeSeparator(final String input) {
return input + "-";
}
public class Demo {
@FunctionalInterface
private static interface IMagicAlgorithm {
long calculate(long x);
}
private long calculate(long x) {
return x * x;
}
private class Inner {
public void foo() {
// obliczenia zawarte w lambda - slabe do utrzymania
IMagicAlgorithm algorithm1 = x -> x * x;
// wywolanie istenijacej metody - nadal nadmiarowy kod
IMagicAlgorithm algorithm2 = x -> calculate(x);
// referencja do metody!
IMagicAlgorithm algorithm3 = Demo.this::calculate;
}
}
}
wyrażenia lambda
jako inversion of control
Zestaw operacji jest nich przedstawiany bez ujawniania tego jak są zaimplementowane. Mówimy co ma być zrobione a nie jak.
Kod zmienia stan programu, korzystamy z zmiennych. Musimy określić nie tylko co ma być zrobione ale również jak.
users.forEach(System.out::print);
// nie interesuje mnie czy
// w metodzie jest for,
// for-each czy while
// mowie co ma być zrobione
for (int i = 0; i < users.size(); i++) {
System.out.println(users.get(i));
}
FluentIterable.from(holdings)
.transformAndConcat(new com.google.common.base.Function<Holding, Iterable<Company>>() {
@Override
public Iterable<Company> apply(Holding input) {
return input.getCompanies();
}
})
.transformAndConcat(new com.google.common.base.Function<Company, Iterable<User>>() {
@Override
public Iterable<User> apply(Company input) {
return input.getUsers();
}
})
.filter(new com.google.common.base.Predicate<User>() {
@Override
public boolean apply(User input) {
return input.getAge() > 18;
}
})
.transform(new com.google.common.base.Function<User, String>() {
@Override
public String apply(User input) {
return input.getFirstName() + " " + input.getLastName();
}
})
.toSortedList(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}); // 31 linijka !
FluentIterable.from(holdings)
.transformAndConcat(new com.google.common.base.Function<Holding, Iterable<Company>>() {
@Override
public Iterable<Company> apply(Holding input) {
return input.getCompanies();
}
})
.transformAndConcat(new com.google.common.base.Function<Company, Iterable<User>>() {
@Override
public Iterable<User> apply(Company input) {
return input.getUsers();
}
})
.filter(new com.google.common.base.Predicate<User>() {
@Override
public boolean apply(User input) {
return input.getAge() > 18;
}
})
.transform(new com.google.common.base.Function<User, String>() {
@Override
public String apply(User input) {
return input.getFirstName() + " " + input.getLastName();
}
})
.toSortedList(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}); // 31 linijka !
holdings
.stream()
.flatMap(holding -> holding.getCompanies().stream())
.flatMap(company -> company.getUsers().stream())
.filter(user -> user.getAge() > 18)
.map(input -> input.getFirstName() + " " + input.getLastName())
.sorted()
.collect(toList())
List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
if(t.getType() == Transaction.GROCERY){
groceryTransactions.add(t);
}
}
Collections.sort(groceryTransactions, new Comparator(){
public int compare(Transaction t1, Transaction t2){
return t2.getValue().compareTo(t1.getValue());
}
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
transactionsIds.add(t.getId());
}
List<Integer> transactionsIds =
transactions.stream()
.filter(t -> t.getType() == Transaction.GROCERY)
.sorted(comparing(Transaction::getValue).reversed())
.map(Transaction::getId)
.collect(toList());
source: http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html
public static String fooWithStream() {
return holdings.stream()
.flatMap(holding -> holding.getCompanies().stream())
.flatMap(company -> company.getUsers().stream())
.map(User::getFirstName)
.distinct()
.sorted()
.collect(Collectors.joining(" "));
}
public static String fooWithoutStream() {
final List<String> result = new LinkedList<>();
String nameList = "";
for (int i = 0; i < holdings.size(); i++) {
for (int j = 0; j < holdings.get(i).getCompanies().size(); ++i /** blad */) {
for (int k = 0; k < holdings.get(i).getCompanies().get(j).getUsers().size(); k++) {
User user = holdings.get(i).getCompanies().get(j).getUsers().get(k);
if (!result.contains(user.getFirstName())) {
result.add(user.getFirstName());
}
}
}
}
result.sort(new Comparator<String>() {
@Override
public int compare(final String o1, final String o2) {
return o1.compareTo(o2);
}
});
for (String name : result) {
nameList += name + " ";
}
return nameList.trim();
}
static Function<String, String> initUserName = (param) -> "userName2:" + param;
private static String getLassName(final Function<String, String> callback) {
final Function<String, String> afterFn = element -> element.toLowerCase();
return callback
.andThen(afterFn)
.apply(" Kowalski");
}
List<Integer> list = Arrays.asList(1, 2, 3);
// Old school
for (Integer i : list)
for (int j = 0; j < i; j++)
System.out.println(i * j);
// "Modern"
list
.forEach(i -> {
IntStream
.range(0, i)
.forEach(j -> {
System.out.println(i * j);
});
});
return apiChain -> apiChain
.prefix("games",
chain -> chain
.prefix("game", games ->
games.post(":id", ctx -> {
final String gameUUID = ctx.getPathTokens().get("id");
renderSecure(ctx, sess -> getSessionCompletionStageFunction(sess, gameUUID));
})
)
.prefix("games", games ->
games.all(noGameId ->
noGameId.byMethod(m -> m.get(listGames(noGameId)).post(createGame(noGameId))))
)
.prefix("players", moves ->
moves.post(":id", ctx -> {
final String gameId = ctx.getPathTokens().get("id");
ctx.getRequest().getBody()
.then(body -> {
final float targetY = Float.parseFloat(body.getText());
renderSecure(ctx, session ->
gamesRepo.movePaddle(gameId, session.userId, targetY));
});
}))
.prefix("stream", stream ->
stream.get(":id", ctx -> {
final String gameId = ctx.getPathTokens().get("id");
final Option<Flowable<GameState>> gsOpt = Option.of(this.gamesFlow.get(gameId));
gsOpt.forEach(gsFlow -> WebSockets
.websocketBroadcast(ctx, gsFlow
.map(val -> chain.getRegistry()
.get(ObjectMapper.class)
.writeValueAsString(val))));
})));
based on: https://github.com/javaFunAgain/ratpong
Domknięcia to funkcje których funkcje wewnętrzne odwołują się do niezależnych (wolnych) zmiennych. Innymi słowy, funkcje zdeklarowane wewnątrz domknięcia 'pamiętają' środowisko w którym zostały utworzone.
function makeAdder(x) {
return function(y) {
return x + y;
};
}
const makeAdder = x => y => x + y;
const add5 = makeAdder(5);
console.log(add5(2)); // 7
public class Main {
private static Function<Integer, Integer> makeAdder(final Integer x) {
return y -> x + y;
}
public static void main(String[] args) {
final Function<Integer, Integer> adder = makeAdder(10);
int result = adder.apply(5);
System.out.println(result);
}
}
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Składanie funkcji pozwala nam budować z funkcji, które pełnią rolę cegiełek, pełnowartościowe aplikacje.
public class Demo {
private static final BigDecimal USDToPLNRate = new BigDecimal("3.86");
private static final BigDecimal amountOfDollar1 = new BigDecimal("23");
private static final BigDecimal amountOfDollar2 = new BigDecimal("43");
@FunctionalInterface
private interface ExchangeRateConverter extends BiFunction<BigDecimal, BigDecimal, BigDecimal> {
default Function<BigDecimal, BigDecimal> curryRate(BigDecimal t) {
return u -> apply(t, u);
}
}
public static void main(final String... args) {
final Function<BigDecimal, BigDecimal> converter = ((ExchangeRateConverter) BigDecimal::multiply)
.curryRate(USDToPLNRate) // currying - rozwijanie funkcji
.andThen(Demo::afterConversion)
.compose(Demo::beforeConversion);
converter.apply(amountOfDollar1);
converter.apply(amountOfDollar2);
}
private static BigDecimal beforeConversion(BigDecimal amount) {
System.out.println("Kwota przed konwersja:" + amount);
return amount;
}
private static BigDecimal afterConversion(BigDecimal amount) {
System.out.println("Po konwersji:" + amount);
return amount;
}
}
public Properties foo(final Properties input) throws Exception {
logger.info("start, with params: {}", input);
final Connection dbConnection = getDBConnection();
Properties result = new Properties();
try {
validateInput(input);
result = callDbProcedure(input, dbConnection);
if(result.getProperty("error") != null) {
throw new Exception(result.getProperty("error"));
}
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println("Rolling back...");
dbConnection.rollback();
} finally {
dbConnection.close();
}
logger.info("end");
return result;
}
public void shoulDoSomething(final Properties input ) throws Exception {
runInTransaction( connection-> {
validateInput(input);
return callDbProcedure(input, connection);
});
}
public Properties runInTransaction(final Function<Connection, Properties> callback) throws Exception {
logger.info("start, with params: {}", input);
final Connection dbConnection = getDBConnection();
Properties result = new Properties();
try {
result = callback.apply(dbConnection);
if(result.getProperty("error") != null) {
throw new IllegalStateException(result.getProperty("error"));
}
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println("Rolling back...");
dbConnection.rollback();
} finally {
dbConnection.close();
}
logger.info("end");
return result;
}
BootstrapMethods:
0: #35 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;
Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;) Ljava/lang/invoke/CallSite;
Method arguments:
#36 (Ljava/lang/Object;)V
#37 invokestatic Main.lambda$main$0:(Ljava/lang/String;)V
#38 (Ljava/lang/String;)V
private static void lambda$1(String item){ //generated by Java compiler
System.out.println(item);
}
private static CallSite bootstrapLambda(Lookup lookup, String name, MethodType type){ //
// lookup = provided by VM
// name = "lambda$1", provided by VM
// type = String -> void
MethodHandle lambdaImplementation = lookup.findStatic(lookup.lookupClass(), name, type);
return LambdaMetafactory.metafactory(lookup, "accept",
//signature of lambda factory
MethodType.methodType(Consumer.class),
//signature of method Consumer.accept after type erasure
MethodType.methodType(void.class, Object.class),
//reference to method with lambda body
lambdaImplementation,
type);
}
void printElements(List<String> strings){
Consumer<String> lambda = invokedynamic(#bootstrapLambda)
strings.forEach(lambda);
}
Imagine a programming language without variables. A language without function parameters and return values. A language without floating point numbers, maybe even without multiplication and division. A language without type casting. Without types, actually.
Paulo Asemblelho
public class Main {
final static IntConsumer r = (i) -> System.out.print("");
public static void main(String[] args) throws IOException {
Main main = new Main();
for (int i = 0; i < 90_0000; ++i) {
main.runRunnable();
}
}
public void runRunnable() {
IntStream.range(0, 2).forEach(Main.r);
}
}
-XX:CompileCommand=print,*Main.runRunnable
-XX:+UnlockDiagnosticVMOptions
Skąd ta liczba?
@ 0 Main::lambda$static$0 (9 bytes) inline (hot)
Compiled method (c2) 195 235 4 Generic::lambda$static$0 (9 bytes)
-XX:+PrintInlining
ompilerOracle: print Main.*
Compiled method (c1) 211 221 3 Main::lambda$static$0 (9 bytes)
total in heap [0x00007fcfa115f450,0x00007fcfa115f808] = 952
relocation [0x00007fcfa115f578,0x00007fcfa115f5b8] = 64
main code [0x00007fcfa115f5c0,0x00007fcfa115f6a0] = 224
stub code [0x00007fcfa115f6a0,0x00007fcfa115f748] = 168
oops [0x00007fcfa115f748,0x00007fcfa115f750] = 8
metadata [0x00007fcfa115f750,0x00007fcfa115f760] = 16
scopes data [0x00007fcfa115f760,0x00007fcfa115f780] = 32
scopes pcs [0x00007fcfa115f780,0x00007fcfa115f7f0] = 112
dependencies [0x00007fcfa115f7f0,0x00007fcfa115f7f8] = 8
nul chk table [0x00007fcfa115f7f8,0x00007fcfa115f808] = 16
Loaded disassembler from /usr/java/jdk1.8.0_151/jre/lib/amd64/hsdis-amd64.so
Decoding compiled method 0x00007fcfa115f450:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x00007fcf9b42ee18} 'lambda$static$0' '(I)V' in 'Main'
# parm0: rsi = int
# [sp+0x40] (sp of caller)
: mov %eax,-0x14000(%rsp)
: push %rbp
: sub $0x30,%rsp
: movabs $0x7fcf9b42f250,%rdx
;{metadata(method data for {method} {0x00007fcf9b42ee18} 'lambda$static$0' '(I)V' in 'Main')}
: mov 0xdc(%rdx),%edi
: add $0x8,%edi
: mov %edi,0xdc(%rdx)
: movabs $0x7fcf9b42ee18,%rdx
;{metadata({method} {0x00007fcf9b42ee18} 'lambda$static$0' '(I)V' in 'Main')}
: and $0x1ff8,%edi
: cmp $0x0,%edi
: je 0x00007fcfa115f648
: movabs $0x76c800c78,%rdx ; {oop(a 'java/lang/Class' = 'java/lang/System')}
Brak mechanizmu generowania lambdy
#0
OopMap{off=124}
#1
OopMap{off=154}
#2
OopMap{rsi=Oop off=161}
Compiled method (c1) 215 226 3 Main::runRunnable (14 bytes)
total in heap [0x00007fcfa11639d0,0x00007fcfa1163ea0] = 1232
relocation [0x00007fcfa1163af8,0x00007fcfa1163b48] = 80
main code [0x00007fcfa1163b60,0x00007fcfa1163d00] = 416
stub code [0x00007fcfa1163d00,0x00007fcfa1163db8] = 184
oops [0x00007fcfa1163db8,0x00007fcfa1163dc0] = 8
metadata [0x00007fcfa1163dc0,0x00007fcfa1163dc8] = 8
scopes data [0x00007fcfa1163dc8,0x00007fcfa1163df8] = 48
scopes pcs [0x00007fcfa1163df8,0x00007fcfa1163e88] = 144
dependencies [0x00007fcfa1163e88,0x00007fcfa1163e90] = 8
nul chk table [0x00007fcfa1163e90,0x00007fcfa1163ea0] = 16
Decoding compiled method 0x00007fcfa11639d0:
Code:
....
0x00007fcfa1163c6a: jmpq 0x00007fcfa1163c77
0x00007fcfa1163c6f: addq $0x1,0x118(%rsi)
0x00007fcfa1163c77: movabs $0x76cd84c18,%rdx ; {oop(a 'Main$$Lambda$1')}
0x00007fcfa1163c81: mov %rax,%rsi ;*invokeinterface forEach
; - Main::runRunnable@8 (line 17)
....
0x00007f65ad16c61a: mov %rax,%rsi
0x00007f65ad16c61d: jmp 0x00007f65ad16c622 ;*invokestatic lambda$static$0
; - Main$$Lambda$1/1831932724::accept@1
; - java.util.stream.Streams$RangeIntSpliterator::forEachRemaining@44 (line 110)
; - java.util.stream.IntPipeline$Head::forEach@15 (line 557)
; - Main::runRunnable@8 (line 17)
; - Main::main@17 (line 12)
private static CallSite cs;
void printElements(List<String> strings){
Consumer<String> lambda;
//begin invokedynamic
if(cs == null)
cs = bootstrapLambda(
MethodHandles.lookup(),
"lambda$1",
MethodType.methodType(void.class, String.class)
);
lambda = (Consumer<String>) cs.getTarget().invokeExact();
//end invokedynamic
strings.forEach(lambda);
}
void printElements(List<String> in) {
in.forEach(it -> System.out.println(it));
}
void printElements(List<String> strings){
Consumer<String> lambda = invokedynamic(#bootstrapLambda)
strings.forEach(lambda);
}
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(caller, invokedType,
invokedName, samMethodType,
implMethod, instantiatedMethodType,
false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
lambdaClassName = targetClass
.getName()
.replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
public class Main {
public static void main(String[] args) {
Function<String, String> identity = str -> str;
// class Main$$Lambda$1/81628611
System.out.println(identity.getClass());
// interface java.util.function.Function
Arrays.asList(identity.getClass().getInterfaces()).forEach(System.out::println);
}
}
Java 7
The Java 7 release introduced a new very important feature into the JVM and Java standard library – method handles. Method handle is a typed, directly executable reference to an underlying method, constructor or field (or similar low-level operation) with optional transformations of arguments or return values. They are in many respects better alternative to method invocations performed using the Reflection API.
public class Main {
public static void printNumber(final int number) {
System.out.println("My number is:" + number);
}
public static void main(String[] args) throws Throwable {
final Method printHello = Main.class.getMethod("printNumber", int.class);
printHello.invoke(Main.class, 1);
}
}
public class Main {
public static void printNumber(final int number) {
System.out.println("My number is:" + number);
}
public static void main(String[] args) throws Throwable {
final MethodType methodType = MethodType.methodType(void.class, int.class);
final MethodHandle methodHandle = MethodHandles
.lookup()
.findStatic(Main.class, "printNumber", methodType);
System.out.println(methodHandle.invoke(1));
}
}
reflective invocation (without setAccessible) 568.506 ns
reflective invocation (with setAccessible) 42.377 ns
methodhandle invocation 27.461 ns
static final methodhandle invocation 9.402 ns
direct invocation 9.363 ns
https://stackoverflow.com/questions/14146570/calling-a-getter-in-java-though-reflection-whats-the-fastest-way-to-repeatedly/14146919#14146919
public class Main {
public static String hello() {
return "Hello";
}
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup caller = MethodHandles.lookup();
MethodType methodType = MethodType.methodType(Object.class);
MethodType actualMethodType = MethodType.methodType(String.class);
MethodType invokedType = MethodType.methodType(Supplier.class);
CallSite site = LambdaMetafactory.metafactory(caller,
"get",
invokedType,
methodType,
caller.findStatic(Main.class, "hello", actualMethodType),
methodType);
MethodHandle factory = site.getTarget();
Supplier<String> r = (Supplier<String>) factory.invoke();
System.out.println(r.get());
}
}
Użycie LambdaMetafactory
source: https://video.oracle.com/detail/videos/featured-videos/video/2623576348001
hot jvm
cold jvm
public static Level get1024 ( final String p ){
return new Level () {
@Override
public Level up () {
return get1023 ( p );
}
};
}
public static Level get1024 ( String p ) {
return () -> get1023 ( p );
}
// () -> () -> () -> () -> () -> ... -> null
how drastically a small problem size and only one iteration can affect the results. The results in this table show how using a problem size of 1000, only running each experiment 100
Performance of Lambda Expressions in Java 8, A. Ward 1 , and D. Deugo 1
School of Computer Science, Carleton University, Ottawa, Ontario, Canada
expression for problems of size 10,000, repeating each experiment 1000 times and then averaging the results
Stream with lambda performance
Example: List<String> upperNames = names
.stream()
.map(name -> name.toUpperCase())
.collect(Collectors.toList());
ArrayList<String> upperNames = new
ArrayList<String>();
for (String name : names) {
upperNames.add(name.toUpperCase());
}
wyrażeń lambda
Function<Integer, Integer> fun1 = x -> x + 2;
Function<Integer, Integer> fun2 = x -> x * x;
Stream.of(fun1, fun2).forEach(f -> f.apply(5));
Function<Integer, Integer> fun1 = x -> x + 2;
Stream.of(fun1, x -> x * x).forEach(f -> f.apply(5));
Stream.of(x -> x + 2, x -> x * x).forEach(f -> f.apply(5));
public class Main {
private static String someText = "unicorn";
private static boolean foo(Predicate<String> param) {
return param.test(someText);
}
private static String foo(Function<String, String> param) {
return param.apply(someText);
}
public static void main(String... args) {
final Predicate<String> stringPredicate = str -> str.length() == 0;
foo(stringPredicate);
final Function<String, String> identity = str -> str;
foo(identity);
}
}
public static void main(String... args) {
foo(str -> str.length() == 0);
foo(str -> str);
}
public class Main {
public final void bar(InterfaceA a) { }
public final void bar(InterfaceB b) { }
public interface InterfaceA {
Double doSomething(Double a);
}
public interface InterfaceB {
Integer doSomething(Integer a);
}
public static void main(String[] args) {
Main foo = new Main();
foo.bar(a -> 1);
foo.bar((Integer a) -> 1);
foo.bar(a -> 1.0);
foo.bar((Double a) -> 1.0);
}
}
public static void main(String[] args) {
ObservableMap<String, String> map = FXCollections.observableMap(new HashMap<>());
// 1. OK
map.addListener(
(MapChangeListener.Change<? extends String, ? extends String> change) -> {}
);
// 2. OK
map.addListener(
(MapChangeListener<String, String>) change -> {}
);
// 3. Ambiguous method call error
map.addListener(change -> {});
}
public interface ObservableMap<K, V> extends Map<K, V>, Observable {
/**
* Add a listener to this observable map.
* @param listener the listener for listening to the list changes
*/
public void addListener(MapChangeListener<? super K, ? super V> listener);
}
now candidate, Java 11?
Phase 1 was forbidding underscore as a lambda formal parameter name in Java 8 (this had no compatibility consequence, since lambdas did not exist previously) and a warning was issued for using underscore as an identifier in other places. Phase 2 came in Java 9, when this warning became an error.
We are now free to complete the planned rehabilitation of underscore to indicate an unused lambda, method, or catch formal parameter.
BiFunction<Integer, String, String> biss = (i, _) -> String.valueOf(i);
Lambda parameters are not allowed to shadow variables in the enclosing scopes. It would be desirable to lift this restriction, and allow lambda parameters (and locals declared with a lambda) to shadow variables defined in enclosing scopes. (One possible argument against is readability.)
Map<String, Integer> msi = ...
String key = computeSomeKey();
msi.computeIfAbsent(key, key -> key.length())
interface X { Iterable m(Iterable<String> arg); }
interface Y { Iterable<String> m(Iterable arg); }
interface Z extends X, Y {} // OK
// Error:(10, 5) java: name clash: m(java.lang.Iterable<java.lang.Integer>) in
// Main.Y and m(java.lang.Iterable<java.lang.String>) in Main.X have the same erasure,
// yet neither overrides the other
interface X { int m(Iterable<String> arg); }
interface Y { int m(Iterable<Integer> arg); }
interface Z extends X, Y {}
@FunctionalInterface
interface MagicFunction extends Function<String, String> { }
// 2. java.lang.ClassCastException
Function<String, String> trim = String::trim;
final MagicFunction fun2 = (MagicFunction) trim;
System.out.println((String::trim).getClass()); // error
// 1. OK
final MagicFunction fun1 = String::trim;
public class Main {
private static Stream<String> getItems() {
return Stream.<String>builder()
.add("John")
.build();
}
public static void main(String[] args) throws Throwable {
for (String item : (Iterable<String>) getItems()::iterator) {
// process items
}
}
}
public class Main {
private static Stream<String> getItems() {
return Stream.<String>builder()
.add("John")
.build();
}
public static <E> Iterable<E> streamOf(Stream<E> stream) {
return stream::iterator;
}
public static void main(String[] args) throws Throwable {
for (String item : streamOf(getItems())) {
// process items
}
}
}
source: Java language specification 9.9-2
interface G1 {
<E extends Exception> Object m() throws E;
}
interface G2 {
<F extends Exception> String m() throws Exception;
}
interface G extends G1, G2 {}
// ...
final G g1 = Main::foo; // OK
final G g2 = () -> null; // ERROR
The function type of G is: <F extends Exception> ()->String throws F
A generic function type for a functional interface may be implemented by a method reference expression (§15.13), but not by a lambda expression (§15.27) as there is no syntax for generic lambda expressions.
var list = new ArrayList<String>(); // infers ArrayList<String>
var stream = list.stream(); // infers Stream<String>
Scanning the OpenJDK code base for local variable declarations, we found that 13% cannot be written using var, since there is no initializer, the initializer has the null type, or (rarely) the initializer requires a target type. Among the remaining local variable declarations:
Main.java:82: error: cannot infer type for local
variable f
var f = () -> { };
^
(lambda expression needs an explicit target-type)
The initializer has no target type (because we haven't inferred it yet). Poly expressions that require such a type, like lambdas, method references, and array initializers, will trigger an error.
public class Main {
static Object lambda$main$0(Object o) {
return null;
}
public static void main(String[] args) throws Throwable {
Function f = str -> str;
lambda$main$0(null);
}
}
/home/lolcio/programowanie/empty-java/src/main/java/Main.java
Error:(5, 19) java: the symbol lambda$main$0(java.lang.Object) conflicts with a compiler-synthesized symbol in Main
Error:(1, 1) java: the symbol lambda$main$0(java.lang.Object) conflicts with a compiler-synthesized symbol in Main
You can serialize a lambda expression if its target type and its captured arguments are serializable. However, like inner classes, the serialization of lambda expressions is strongly discouraged.
https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html#serialization
https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html#serialization
When the Java compiler compiles certain constructs, such as inner classes, it creates synthetic constructs; these are classes, methods, fields, and other constructs that do not have a corresponding construct in the source code. Synthetic constructs enable Java compilers to implement new Java language features without changes to the JVM. However, synthetic constructs can vary among different Java compiler implementations, which means that .class files can vary among different implementations as well. Consequently, you may have compatibility issues if you serialize an inner class and then deserialize it with a different JRE implementation.
public class Syntetic {
private class InnerClass {
private String var = "This is a private variable in InnerClass";
}
public static void main(String[] args) throws NoSuchMethodException {
Syntetic.InnerClass innerClass = new Syntetic().new InnerClass();
System.out.println(innerClass.var);
}
}
~ javap ./Syntetic\$InnerClass.class
Compiled from "Syntetic.java"
class Syntetic$InnerClass {
final Syntetic this$0;
Syntetic$InnerClass(Syntetic, Syntetic$1);
static java.lang.String access$100(Syntetic$InnerClass);
}
static java.lang.String access$100(Syntetic$InnerClass);
descriptor: (LSyntetic$InnerClass;)Ljava/lang/String;
flags: ACC_STATIC, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #1 // Field var:Ljava/lang/String;
4: areturn
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 x0 LSyntetic$InnerClass;
}
public class Syntetic {
private class InnerClass {
private String var = "This is a private variable in InnerClass";
}
public static void main(String[] args) throws NoSuchMethodException {
Syntetic.InnerClass innerClass = new Syntetic().new InnerClass();
System.out.println(innerClass.var);
System.out.println(
innerClass.getClass().getDeclaredMethod("access$100", InnerClass.class));
}
}
This is a private variable in InnerClass
static java.lang.String Syntetic$InnerClass.access$100(Syntetic$InnerClass)
* Hazelcast is an open source in-memory data grid based on Java. It simplify: application scaling, build cache as service, shared storage, in memory computing, memcache, web session clustering etc
HazelcastInstance instance =
Hazelcast.newHazelcastInstance(new Config());
ExecutorService executorService =
instance.getExecutorService("massExecutionWithLambdas");
executorService.submit( () -> System.out.println("Running function") );
/** |
\|/
'
com.hazelcast.nio.serialization.HazelcastSerializationException:
There is no suitable serializer for class .....$$Lambda$2/.....
*/
executorService.submit( (Runnable & Serializable) () -> {
System.out.println("Running function");
} );
Rozwiązanie: Zrzutowanie do dwóch interfejsów jednoczesnie
executorService.submit( () -> {
System.out.println("Running function");
});
Co jest w byte code serializowanej lambdy?
// access flags 0x100A
private static synthetic $deserializeLambda$(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;
....
ICONST_0
INVOKEVIRTUAL java/lang/invoke/SerializedLambda.getCapturedArg (I)Ljava/lang/Object;
CHECKCAST java/io/PrintStream
INVOKEDYNAMIC accept(Ljava/io/PrintStream;)Ljava/util/function/Consumer; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;
Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)V,
// handle kind 0x5 : INVOKEVIRTUAL
java/io/PrintStream.println(Ljava/lang/Object;)V,
(Ljava/lang/Object;)V,
5,
0
]
ARETURN
L4
FRAME CHOP 2
NEW java/lang/IllegalArgumentException
DUP
LDC "Invalid lambda deserialization"
INVOKESPECIAL java/lang/IllegalArgumentException.<init> (Ljava/lang/String;)V
ATHROW
L5
LOCALVARIABLE lambda Ljava/lang/invoke/SerializedLambda; L0 L5 0
MAXSTACK = 3
MAXLOCALS = 3
}
Co jest w byte code serializowanej lambdy?
SerializedLambda has a readResolve method that looks for a (possibly private) static method called $deserializeLambda$(SerializedLambda) in the capturing class, invokes that with itself as the first argument, and returns the result.
Lambda classes implementing $deserializeLambda$ are responsible for validating that the properties of the SerializedLambda are consistent with a lambda actually captured by that class.
public class Generic {
interface Foo {
default void greet() {
System.out.println(this); // Generic$$Lambda$1/931919113
}
}
public static void main(String[] args) {
Foo f = (Consumer & Foo) System.out::println;
f.greet();
}
}
STRUMIENI
Exception in thread "main" java.lang.NullPointerException
at Test$$Lambda$3/455659002.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.forEach(Unknown Source)
at Test.main(Test.java:12)
final List<String> list = Arrays.asList("foo", null, "bar");
list.stream()
.map(Function.identity())
.filter(x -> true)
.map(String::length)
.forEach(System.out::println);
Stream
.of("one", "two", "three", "four")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
Intellij plugin
Czas na pytania
https://slides.com/kamillolo/secrets-of-lambda