Lambda-Expression
# Functional Interfaces
- A functional interface is an interface with a single abstract method
- A functional interface represent a function
- It doesn't matter if we have many default or even static methods in our interface, as long as we have a single abstract method, we refer to it as a functional interface
- e.g. Comparable Interface
- Only abstract method:
compareTo(T o)
- Only abstract method:
public interface Printer {
void print(String message);
}
// this is unnecessary
public class ConsolePrinter implements Printer {
@Override
public void print(String message) {
System.out.println(message);
}
}
public class LambdasDemo {
public static void show() {
greet(new ConsolePrinter());
}
public static void greet(Printer printer) {
printer.print("Hello world");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Anonymous Inner Classes
- Write less code, but not enough, that's why we have lambda expression
public class LambdasDemo {
public static void show() {
// this is the anonymous inner class
// it doesn't have a name
greet(new Printer() {
@Override
public void print(String message) {
System.out.println(message);
}
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Lambda Expressions
- By using lambda expression, it can be implemented in a standalone function
- A function exists on its on without belonging to a class
- It is like an anonymous function that we can pass around
public class LambdasDemo {
public static void show() {
// it know the type of message
// if only one parametor, don't need use parenthesis
greet(message -> { System.out.println(message)});
// or
// lambda expressions are essentially objects
// and can be used to represent anonymous in ner classes
Printer printer = message -> System.out.println(message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Variable Capture
- One of the differences between Lambda Expression and Anonymous Inner Classes:
- Lambda Expression:
thisreferences the enclosing object- We cannot have fields because it just represent an anonymous function
- So we cannot have instance fields here and cannot store state here
- We can only access the local variables declared in the closing object as well the static or instance fields in the class
- Anonymous Inner Classes:
thisreferences the current instance of the anonymous inner class- It can have state, they can have field to store some data
- Lambda Expression:
public class LambdasDemo {
public String prefix = "-";
public void show() {
greet(message -> { System.out.println(this.prefix + message)});
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# Method References
- Makes code more compact and easier to read
- Sometimes all we do in a lambda expression is just passing the parameter to an existing method
- Therefore, it's easier to reference the method directly: (three parts)
ClassName/the object that contains this method::name of the method without parenthesis
- With this method references, we can reference static or instance methods, as well as constructors
public class LambdasDemo {
public static void output(String message) {}
public void out(String message){}
public LambdasDemo(String message) {}
public static void show() {
greet(message -> { System.out.println(message)});
// simpler way:
greet(System.out::print);
// it is a static method
greet(message -> output(message));
// becomes:
greet(LambdasDemo::output)
// it is an instance method
var demo = new LambdasDemo();
greet(message -> demo.out(message));
// becomes
greet(demo::out);
// for constructor
greet(message -> new LambdasDemo(message));
// becomes
greet(LambdasDemo::new);
}
public static void greet(Printer printer) {
printer.print("Hello world");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Built-in Functional Interfaces
- Java provides many predefined functional interfaces that we can use to perform common tasks
- These interfaces are declared in
java.util.function - There are four types of functional interfaces
- Consumer
- Supplier
- Function
- Predicate
# The Consumer Interfaces
- It represents an operation that takes a single argument and returns no results
- It is called Consumer, because it consumes a value
BiConsumertakes two valuesIntConsumertakes int values (more efficient when dealing with a large number of numbers)- e.g.
void consume(obj)
// it just consumes
public interface Printer {
void print(String message);
}
1
2
3
4
2
3
4
public class LambdasDemo {
public void show() {
List<Integer> list = List.of(1, 2, 3);
// first way: imperative programming: implementing logic using instructions
// (for, if/else, switch/case)
for (var item : list)
System.out.println(item);
// second way: declarative programming
// instead of specifying how things should be done
// we specify what is to be done
// it expects a consumer object
list.forEach(item - > System.out.println(item));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Chaining Consumer
public class LambdasDemo {
public void show() {
List<String> list = List.of("a", "b", "c");
Consumer<String> print = item -> System.out.println(item);
Consumer<String> printUpperCase = item -> System.out.println(item.toUpperCase());
// print three times
list.forEach(print.andThen(printUpperCase).andThen(print));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# The Supplier Interface
- It is the opposite of the Consumer Interface
- It represents an operation that takes no input and returns a value so it supplies a value
- e.g.
obj supply()
public class LambdasDemo {
public void show() {
List<String> list = List.of("a", "b", "c");
Supplier<Double> geRandom = () -> {return Math.Random()};
Supplier<Double> geRandom = () -> Math.Random();
var random = getRandom.get();
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- Lazy Evaluation: function is not executed until we explicitly call it
# The Function Interface
- The function interface represents a function that can map a value to a different value
- e.g.
obj map(obj)
public class LambdasDemo {
public void show() {
Function<String, Integer> map = str -> str.length();
var length = map.apply("Sky");
}
}
1
2
3
4
5
6
2
3
4
5
6
# Composing Functions
public class LambdasDemo {
public void show() {
// "key:value"
// first: "key=value"
// second: "{key=value}"
Function<String, String> replaceColon = str -> str.replace(":", "=");
Function<String, String> addBraces = str -> "{" + str + "}";
// Decarative Programming
var result = replaceColon
.andThen(addBraces)
.apply("key:value");
// second way in reverse order
result = addBraces.compose(replaceColon).apply("key:value");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# The Predicate Interface
- The Predicate Interface represents an operation that takes an object and checks to see if the object satisfies some criteria
- e.g.
boolean test(condition) - kind
- BiPredicate
- IntPredicate
public class LambdasDemo {
public void show() {
Predicate<String> isLongerThan5 = str -> str.length() > 5;
var result = IslongerThan5.test("sky");
}
}
1
2
3
4
5
6
2
3
4
5
6
# Combining Predicates
public class LambdasDemo {
public void show() {
Predicate<String> hasLeftBrace = str -> str.startsWith("{");
Predicate<String> hasRightBrace = str -> str.endssWith("}");
// && || !
Predicate<String> hasLeftAndRightBraces = hasLeftBrace.and(hasRightBrace);
Predicate<String> hasLeftOrRightBraces = hasLeftBrace.or(hasRightBrace);
Predicate<String> hasEitherLeftOrRightBraces = hasLeftBrace.negate(hasRightBrace);
var result = hasLeftAndRightBraces.test("{}"); // true
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# The BinaryOperator Interface
- It extends from the
BiFunction<T,T,T>interface- It takes two types of type
Tand returns a result of typeT
- It takes two types of type
public class LambdasDemo {
public void show() {
// more efficient to use the BinaryOperator, since parameters are primitive integers and need to be boxed
BinaryOperator<Integer> add = (a, b) -> a + b;
var result = add.apply(1, 2);
Function<Integer, Integer> square = a -> a * a;
var result2 = add.andThen(square).apply(1, 2);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# The UnaryOperator Interface
- It takes a value of type
Tand returns a result of typeT
public class LambdasDemo {
public void show() {
UnaryOperator<Integer> square = n -> n * n;
UnaryOperator<Integer> increment = n -> n + 1;
var result = increment.andThen(square).apply(1);
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
上次更新: 2022/12/04, 16:55:22