Skip to content

Commit 49f9b8b

Browse files
committed
updated ch02 on Lambdas
1 parent 3500839 commit 49f9b8b

File tree

6 files changed

+176
-22
lines changed

6 files changed

+176
-22
lines changed

02-lambdas.md

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
## Lambdas
1+
Lambdas
2+
-----
23

3-
From today, I am kicking off **7 Days with Java 8** blog series with first blog on Lambdas. One of the most important features in Java 8 is the introduction of Lambda expressions. They make your code concise and allows you to pass behavior around. For some time now, Java is criticized for being verbose and for lacking functional programming capabilities. With functional programming becoming more popular and relevant, Java is forced to embrace the functional style of programming. Else, Java would become irrelevant.
4+
One of the most important features in Java 8 is the introduction of Lambda expressions. They make your code concise and allows you to pass behavior around. For some time now, Java is criticized for being verbose and for lacking functional programming capabilities. With functional programming becoming more popular and relevant, Java is forced to embrace the functional style of programming. Else, Java would become irrelevant.
45

56
Java 8 is a big step forward in making the world's most popular language adopt the functional style of programming. To support functional programming style, a language needs to support functions as first class citizen. Prior to Java 8, writing a clean functional style code was not possible without the use of an anonymous inner class boilerplate. With the introduction of Lambda expressions, functions have become first class citizen and they can be passed around just like any other variable.
67

78
Lambda expressions allow you to define an anonymous function that is not bound to an identifier. You can use them like any other construct in your programming language like variable declaration. Lambda expressions are required if a programming language needs to support higher order functions. Higher order functions are functions that either accept other functions as arguments or returns a function as a result.
89

10+
> Code for this section is inside [ch02 package](https://github.com/shekhargulati/java8-the-missing-tutorial/tree/master/code/src/main/java/com/shekhargulati/java8_tutorial/ch02).
11+
912
Now, with the introduction of Lambda expressions in Java 8, Java supports higher order functions. Let us look at the canonical example of Lambda expression -- a sort function in Java's `Collections` class. The `sort` function has two variants -- one that takes a `List` and another that takes a `List` and a `Comparator`. The second `sort` function is an example of a Higher order function that accepts a lambda expression as shown below in the code snippet.
1013

1114
```java
@@ -27,7 +30,7 @@ The expression `(first, second) -> first.length() - second.length()` shown above
2730

2831
Before we dig deeper into Java 8 Lambdas support, let's look into their history to understand why they exist.
2932

30-
### History of Lambdas
33+
## History of Lambdas
3134

3235
Lambda expressions have their roots in the Lambda Calculus. [Lambda calculus](https://en.wikipedia.org/wiki/Lambda_calculus) originated from the work of [Alonzo Church](https://en.wikipedia.org/wiki/Alonzo_Church) on formalizing the concept of expressing computation with functions. Lambda calculus is turing complete formal mathematical way to express computations. Turing complete means you can express any mathematical computation via lambdas.
3336

@@ -47,7 +50,7 @@ The main concept in Lambda calculus is expressions. An expression can be express
4750

4851
Now, you understand Lambda calculus and its impact on functional programming languages. Let's learn how they are implemented in Java 8.
4952

50-
### Passing behavior before Java 8
53+
## Passing behavior before Java 8
5154

5255
Before Java 8, the only way to pass behavior was to use anonymous classes. Suppose you want to send an email in another thread after user registration. Before Java 8, you would write code like one shown below.
5356

@@ -59,6 +62,7 @@ sendEmail(new Runnable() {
5962
}
6063
});
6164
```
65+
6266
The sendEmail method has following method signature.
6367

6468
```java
@@ -75,9 +79,9 @@ Iterable<Task> lambdaTasks = Iterables.filter(tasks, new Predicate<Task>() {
7579
}
7680
});
7781
```
78-
With Java 8 Stream API, you can write the above mentioned code without the use of a third party library like Guava. We will cover streams on Day 2. So, stay tuned!!
82+
With Java 8 Stream API, you can write the above mentioned code without the use of a third party library like Guava. We will cover streams in [chapter 3](./03-streams.md). So, stay tuned!!
7983

80-
### Java 8 Lambda expressions
84+
## Java 8 Lambda expressions
8185

8286
In Java 8, we would write the code using a lambda expression as show below. We have mentioned the same example in the code snippet above.
8387

@@ -87,11 +91,12 @@ sendEmail(() -> System.out.println("Sending email..."));
8791

8892
The code shown above is concise and does not pollute the programmer's intent to pass behavior. `()` is used to represent no function parameters i.e. `Runnable` interface `run` method does not have any parameters. `->` is the lambda operator that separates the parameters from the function body which prints `Sending email...` to the standard output.
8993

90-
Let's look at the Collections.sort example again so that we can understand how lambda expressions work with the parameters. To sort a List of names by their length, we used Collections.sort function as shown below.
94+
Let's look at the Collections.sort example again so that we can understand how lambda expressions work with the parameters. To sort a list of names by their length, we passed a `Comparator` to the sort function. The `Comparator` is shown below.
9195

9296
```java
9397
Comparator<String> comparator = (first, second) -> first.length() - second.length();
9498
```
99+
95100
The lambda expression that we wrote was corresponding to compare method in the Comparator interface. The signature of `compare` function is shown below.
96101

97102
```java
@@ -107,28 +112,30 @@ In the lambda expression, we didn't have to explicitly provide the type -- Strin
107112
Comparator comparator = (first, second) -> first.length() - second.length(); // compilation error - Cannot resolve method 'length()'
108113
```
109114

110-
### How does Lambda expressions work in Java 8?
115+
## How does Lambda expressions work in Java 8?
111116

112-
You may have noticed that the type of a lambda expression is some interface like Comparator in the above example. You can't use any interface with lambda expression. Only those interfaces which have only one non-object abstract method can be used with lambda expressions. These kinds of interfaces are called **Functional interfaces** and they are annotated with `@FunctionalInterface` annotation. Runnable interface is an example of functional interface as shown below. `@FunctionalInterface` annotation is not mandatory but, it can help tools know that an interface is a functional interface and perform meaningful actions. For example, if you try to compile an interface that annotates itself with `@FunctionalInterface` annotation and has multiple abstract methods then compilation will fail with an error ***Multiple non-overriding abstract methods found***. Similarly, if you add `@FunctionInterface` annotation to an interface without any method i.e. a marker interface then you will get error message ***No target method found***.
117+
You may have noticed that the type of a lambda expression is some interface like Comparator in the above example. You can't use any interface with lambda expression. ***Only those interfaces which have only one non-object abstract method can be used with lambda expressions***. These kinds of interfaces are called **Functional interfaces** and they can be annotated with `@FunctionalInterface` annotation. Runnable interface is an example of functional interface as shown below.
113118

114119
```java
115120
@FunctionalInterface
116121
public interface Runnable {
117122
public abstract void run();
118123
}
119-
120124
```
121125

122-
Let's answer one of the most important questions that might be coming to your mind. Are Java 8 lambda expressions just the syntactic sugar over anonymous inner classes or how does functional interface gets translated to bytecode?
126+
`@FunctionalInterface` annotation is not mandatory but, it can help tools know that an interface is a functional interface and perform meaningful actions. For example, if you try to compile an interface that annotates itself with `@FunctionalInterface` annotation and has multiple abstract methods then compilation will fail with an error ***Multiple non-overriding abstract methods found***. Similarly, if you add `@FunctionInterface` annotation to an interface without any method i.e. a marker interface then you will get error message ***No target method found***.
127+
128+
Let's answer one of the most important questions that might be coming to your mind. ***Are Java 8 lambda expressions just the syntactic sugar over anonymous inner classes or how does functional interface gets translated to bytecode?***
123129

124130
The short answer is **NO**. Java 8 does not use anonymous inner classes mainly for two reasons:
125131

126132
1. **Performance impact**: If lambda expressions were implemented using anonymous classes then each lambda expression would result in a class file on disk. When these classes are loaded by JVM at startup, then startup time of JVM will increase as all the classes needs to be first loaded and verified before they can be used.
133+
127134
2. **Possibility to change in future**: If Java 8 designers would have used anonymous classes from the start then it would have limited the scope of future lambda implementation changes.
128135

129-
#### Using invokedynamic
136+
### Using invokedynamic
130137

131-
Java 8 designers decided to use invokedynamic instruction added in Java 7 to defer the translation strategy at runtime. When`javac` compiles the code, it captures the lambda expression and generates an invokedynamic call site (called lambda factory). The invokedynamic call site when invoked returns an instance of the Functional Interface to which the lambda is being converted. For example, if we look at the byte code of our Collections.sort example, it will look like as shown below.
138+
Java 8 designers decided to use `invokedynamic` instruction added in Java 7 to defer the translation strategy at runtime. When`javac` compiles the code, it captures the lambda expression and generates an `invokedynamic` call site (called lambda factory). The `invokedynamic` call site when invoked returns an instance of the functional interface to which the lambda is being converted. For example, if we look at the byte code of our Collections.sort example, it will look like as shown below.
132139

133140
```
134141
public static void main(java.lang.String[]);
@@ -163,49 +170,59 @@ public static void main(java.lang.String[]);
163170

164171
The interesting part of the byte code shown above is the line 23 `23: invokedynamic #7, 0 // InvokeDynamic #0:compare:()Ljava/util/Comparator;` where a call to `invokedynamic` is made.
165172

166-
The second step is to convert the body of the lambda expression into a method that will be invoked through the invokedynamic instruction. This is the step where JVM implementers have the liberty to choose their own strategy.
173+
The second step is to convert the body of the lambda expression into a method that will be invoked through the `invokedynamic` instruction. This is the step where JVM implementers have the liberty to choose their own strategy.
167174

168175
I have only glossed over this topic. You can read about internals at http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html.
169176

170-
### Anonymous classes vs lambdas
177+
## Anonymous classes vs lambdas
178+
179+
Let's compare anonymous classes with lambdas to understand the differences between them.
171180

172181
1. In anonymous classes, `this` refers to the anonymous class itself whereas in lambda expression `this` refers to the class enclosing the lambda expression.
173182

174183
2. You can shadow variables in the enclosing class inside the anonymous class. This gives compile time error when done inside lambda expression.
175184

176185
3. Type of the lambda expression is determined from the context where as type of the anonymous class is specified explicitly as you create the instance of anonymous class.
177186

178-
### Do I need to write my own functional interfaces?
187+
## Do I need to write my own functional interfaces?
179188

180189
By default, Java 8 comes with many functional interfaces which you can use in your code. They exist inside `java.util.function` package. Let's have a look at few of them.
181190

182-
#### java.util.function.Predicate<T>
191+
### java.util.function.Predicate<T>
183192

184193
This functional interface is used to define check for some condition i.e. a predicate. Predicate interface has one method called `test` which takes a value of type `T` and return boolean. For example, from a list of `names` if we want to filter out all the names which starts with **s** then we will use a predicate as shown below.
185194

186195
```java
187196
Predicate<String> namesStartingWithS = name -> name.startsWith("s");
188197
```
189198

190-
#### java.util.function.Consumer<T>
199+
### java.util.function.Consumer<T>
191200

192201
This functional interface is used for performing actions which does not produce any output. Predicate interface has one method called `accept` which takes a value of type `T` and return nothing i.e. it is void. For example, sending an email with given message.
193202

194203
```java
195204
Consumer<String> messageConsumer = message -> System.out.println(message);
196205
```
197206

198-
#### java.util.function.Function<T,R>
207+
### java.util.function.Function<T,R>
199208

200209
This functional interface takes one value and produces a result. For example, if we want to uppercase all the names in our `names` list, we can write a Function as shown below.
201210

202211
```java
203212
Function<String, String> toUpperCase = name -> name.toUpperCase();
204213
```
205214

206-
We will cover more functional interfaces as we move along in the series.
215+
### java.util.function.Supplier<T>
216+
217+
This functional interface does not take any input but produces a value. This could be used to generate unique identifiers as shown below.
218+
219+
```java
220+
Supplier<String> uuidGenerator= () -> UUID.randomUUID().toString();
221+
```
222+
223+
We will cover more functional interfaces throughout this tutorial.
207224

208-
### Method references
225+
## Method references
209226

210227
There would be times when you will be creating lambda expressions that only calls a specific method like `Function<String, Integer> strToLength = str -> str.length();`. The lambda only calls `length()` method on the `String` object. This could be simplified using method references like `Function<String, Integer> strToLength = String::length;`. They can be seen as shorthand notation for lambda expression that only calls a single method. In the expression `String::length`, `String` is the target reference, `::` is the delimiter, and `length` is the function that will be called on the target reference. You can use method references on both the static and instance methods.
211228

@@ -217,7 +234,7 @@ Suppose we have to find a maximum number from a list of numbers then we can writ
217234

218235
This is used for method reference to an instance method for example `String::toUpperCase` calls `toUpperCase` method on a `String` reference. You can also use method reference with parameters for example `BiFunction<String, String, String> concatFn = String::concat`. The `concatFn` can be called as `concatFn.apply("shekhar", "gulati")`. The `String` `concat` method is called on a String object and passed a parameter like `"shekhar".concat("gulati")`.
219236

220-
### Exercise >> Lambdify me
237+
## Exercise >> Lambdify me
221238

222239
Let's look at the code shown below and apply what we have learnt so far.
223240

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.shekhargulati.java8_tutorial.ch02;
2+
3+
import java.util.Arrays;
4+
import java.util.Collections;
5+
import java.util.Comparator;
6+
import java.util.List;
7+
8+
public class Example1_Lambda {
9+
10+
public static void main(String[] args) {
11+
List<String> names = Arrays.asList("shekhar", "rahul", "sameer");
12+
// sort alphabetically
13+
Collections.sort(names);
14+
System.out.println("names sorted alphabetically >>");
15+
System.out.println(names);
16+
System.out.println();
17+
18+
// using anonymous classes
19+
Collections.sort(names, new Comparator<String>() {
20+
@Override
21+
public int compare(String o1, String o2) {
22+
return o1.length() - o2.length();
23+
}
24+
});
25+
System.out.println("names sorted by length >>");
26+
System.out.println(names);
27+
System.out.println();
28+
29+
/**
30+
* Using lambda
31+
* Things to show >>
32+
* 1. return statement
33+
* 2. Without return statement
34+
* 3. Multiple lines
35+
* 4. Type inference
36+
*/
37+
38+
Collections.sort(names, (String first, String second) -> second.length() - first.length());
39+
System.out.println("names sorted by length(reversed) >>");
40+
System.out.println(names);
41+
System.out.println();
42+
}
43+
44+
45+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.shekhargulati.java8_tutorial.ch02;
2+
3+
/**
4+
*
5+
* Example of compiler unable to detect lambda expression types
6+
*/
7+
public class Example2_Lambda {
8+
9+
public static void main(String[] args) {
10+
11+
// Comparator comparator = (first, second) -> first.length() - second.length();
12+
13+
14+
}
15+
}
16+
17+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.shekhargulati.java8_tutorial.ch02;
2+
3+
import java.util.UUID;
4+
import java.util.function.Consumer;
5+
import java.util.function.Function;
6+
import java.util.function.Predicate;
7+
import java.util.function.Supplier;
8+
9+
public class Example3_Functionalnterfaces {
10+
11+
public static void main(String[] args) {
12+
13+
Predicate<String> nameStartWithS = name -> name.startsWith("s");
14+
15+
Consumer<String> sendEmail = message -> System.out.println("Sending email >> " + message);
16+
17+
Function<String, Integer> stringToLength = name -> name.length();
18+
19+
Supplier<String> uuidSupplier = () -> UUID.randomUUID().toString();
20+
21+
}
22+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.shekhargulati.java8_tutorial.ch02;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.function.Function;
8+
9+
import static java.util.Comparator.comparingInt;
10+
11+
public class Example4_MethodReferences {
12+
13+
public static void main(String[] args) {
14+
List<String> names = Arrays.asList("shekhar", "rahul", "sameer");
15+
16+
List<Integer> namesLength = transform(names, String::length);
17+
System.out.println(namesLength);
18+
19+
List<String> upperCaseNames = transform(names, String::toUpperCase);
20+
System.out.println(upperCaseNames);
21+
22+
List<Integer> numbers = transform(Arrays.asList("1", "2", "3"), Integer::parseInt);
23+
System.out.println(numbers);
24+
25+
Collections.sort(names, comparingInt(String::length).reversed());
26+
System.out.println(names);
27+
}
28+
29+
private static <T, R> List<R> transform(List<T> list, Function<T, R> fx) {
30+
List<R> result = new ArrayList<>();
31+
for (T element : list) {
32+
result.add(fx.apply(element));
33+
}
34+
return result;
35+
}
36+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.shekhargulati.java8_tutorial.ch02;
2+
3+
/**
4+
* Example of @FunctionalInterface
5+
*/
6+
7+
@FunctionalInterface
8+
public interface InvalidFunctionInterface {
9+
10+
public boolean test();
11+
12+
// public boolean test1();
13+
14+
15+
}
16+
17+

0 commit comments

Comments
 (0)