You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 02-lambdas.md
+39-22Lines changed: 39 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,11 +1,14 @@
1
-
## Lambdas
1
+
Lambdas
2
+
-----
2
3
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.
4
5
5
6
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.
6
7
7
8
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.
8
9
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
+
9
12
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.
Before we dig deeper into Java 8 Lambdas support, let's look into their history to understand why they exist.
29
32
30
-
###History of Lambdas
33
+
## History of Lambdas
31
34
32
35
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.
33
36
@@ -47,7 +50,7 @@ The main concept in Lambda calculus is expressions. An expression can be express
47
50
48
51
Now, you understand Lambda calculus and its impact on functional programming languages. Let's learn how they are implemented in Java 8.
49
52
50
-
###Passing behavior before Java 8
53
+
## Passing behavior before Java 8
51
54
52
55
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.
53
56
@@ -59,6 +62,7 @@ sendEmail(new Runnable() {
59
62
}
60
63
});
61
64
```
65
+
62
66
The sendEmail method has following method signature.
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!!
79
83
80
-
###Java 8 Lambda expressions
84
+
## Java 8 Lambda expressions
81
85
82
86
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.
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.
89
93
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.
The lambda expression that we wrote was corresponding to compare method in the Comparator interface. The signature of `compare` function is shown below.
96
101
97
102
```java
@@ -107,28 +112,30 @@ In the lambda expression, we didn't have to explicitly provide the type -- Strin
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.
113
118
114
119
```java
115
120
@FunctionalInterface
116
121
publicinterfaceRunnable {
117
122
publicabstractvoidrun();
118
123
}
119
-
120
124
```
121
125
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?***
123
129
124
130
The short answer is **NO**. Java 8 does not use anonymous inner classes mainly for two reasons:
125
131
126
132
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
+
127
134
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.
128
135
129
-
####Using invokedynamic
136
+
### Using invokedynamic
130
137
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.
132
139
133
140
```
134
141
public static void main(java.lang.String[]);
@@ -163,49 +170,59 @@ public static void main(java.lang.String[]);
163
170
164
171
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.
165
172
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.
167
174
168
175
I have only glossed over this topic. You can read about internals at http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html.
169
176
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.
171
180
172
181
1. In anonymous classes, `this` refers to the anonymous class itself whereas in lambda expression `this` refers to the class enclosing the lambda expression.
173
182
174
183
2. You can shadow variables in the enclosing class inside the anonymous class. This gives compile time error when done inside lambda expression.
175
184
176
185
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.
177
186
178
-
###Do I need to write my own functional interfaces?
187
+
## Do I need to write my own functional interfaces?
179
188
180
189
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.
181
190
182
-
####java.util.function.Predicate<T>
191
+
### java.util.function.Predicate<T>
183
192
184
193
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.
185
194
186
195
```java
187
196
Predicate<String> namesStartingWithS = name -> name.startsWith("s");
188
197
```
189
198
190
-
####java.util.function.Consumer<T>
199
+
### java.util.function.Consumer<T>
191
200
192
201
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.
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.
201
210
202
211
```java
203
212
Function<String, String> toUpperCase = name -> name.toUpperCase();
204
213
```
205
214
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.
We will cover more functional interfaces throughout this tutorial.
207
224
208
-
###Method references
225
+
## Method references
209
226
210
227
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.
211
228
@@ -217,7 +234,7 @@ Suppose we have to find a maximum number from a list of numbers then we can writ
217
234
218
235
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")`.
219
236
220
-
###Exercise >> Lambdify me
237
+
## Exercise >> Lambdify me
221
238
222
239
Let's look at the code shown below and apply what we have learnt so far.
0 commit comments