Back to: Java Tutorials
Introduction
Lambda expressions are a powerful feature introduced in Java 8. They provide a concise and expressive way to write code that is more functional and easier to read. In this tutorial, we will explore what lambda expressions are, how they work, and how to use them in Java.
What are Lambda expressions?
Lambda expressions are a way to write inline functions that can be passed around like variables. They allow you to define a block of code that can be executed later, and pass it as an argument to another method or function.
Lambda expressions are a form of anonymous function, which means that they do not have a name or a return type. Instead, they consist of a list of parameters, a lambda operator “->”, and a body of code.
Syntax of Lambda expressions
The syntax of a lambda expression is as follows:
(parameters) -> { body }
Here, parameters are the arguments that the lambda expression takes, and the body is the code that it executes. The “->” operator separates the parameters from the body.
Example:
(int a, int b) -> { return a + b; }
In this example, the lambda expression takes two integer parameters, adds them together, and returns the result.
Java Lambda Expressions Example
Here are detailed code examples with output on the uses of lambda expressions in Java:
Functional Interfaces
Example 1: Using a lambda expression to implement a functional interface with a single abstract method (SAM).
@FunctionalInterface
interface MyFunctionalInterface {
void doSomething(int a, int b);
}
public class LambdaExample {
public static void main(String[] args) {
// Creating a lambda expression and assigning it to a functional interface
MyFunctionalInterface myFunctionalInterface = (a, b) -> System.out.println(a + b);
// Calling the method on the functional interface, which executes the lambda expression
myFunctionalInterface.doSomething(3, 4); // Output: 7
}
}
In this example, we create a functional interface MyFunctionalInterface
with a single abstract method doSomething
. We then create a lambda expression (a, b) -> System.out.println(a + b)
that takes two integer arguments and prints their sum to the console. Finally, we assign the lambda expression to a variable of type MyFunctionalInterface
and call the doSomething
method on it, which executes the lambda expression.
Example 2: Using a lambda expression to implement the Runnable interface.
public class LambdaExample {
public static void main(String[] args) {
// Creating a lambda expression that implements the run method of the Runnable interface
Runnable myRunnable = () -> System.out.println("Hello from Runnable!");
// Creating a new thread and passing the lambda expression as a parameter to the constructor
Thread thread = new Thread(myRunnable);
thread.start(); // Output: Hello from Runnable!
}
}
In this example, we create a lambda expression () -> System.out.println("Hello from Runnable!")
that implements the run
method of the Runnable
interface. We then create a new thread and pass the lambda expression as a parameter to the constructor. When we start the thread, the run
method is executed, which executes the lambda expression and prints “Hello from Runnable!” to the console.
Lambda expressions with Collections
Example 1: Using a lambda expression to iterate over a list and print each element.
rust
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
// Output: Alice, Bob, Charlie
In this example, we create a list of strings names
and use a lambda expression name -> System.out.println(name)
with the forEach
method to iterate over each element of the list and print it to the console.
Example 2: Using a lambda expression with the map method to convert a list of integers to their squares.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println(squares); // Output: [1, 4, 9, 16]
In this example, we create a list of integers numbers
and use the stream
method to create a stream of elements from the list. We then use the map
method with a lambda expression n -> n * n
to square each element of the stream, and use the collect
method with the toList
collector to convert the stream back into a list. Finally, we print the resulting list of squares to the console.
Lambda Expression as the Method References
Lambda expressions in Java can be further simplified by using method references. A method reference is a shorthand syntax that refers to a method of a class or an instance without invoking it. Method references can be used to simplify lambda expressions and make code more concise and readable. In this section, we will explore several examples of using method references with lambda expressions.
Example 1: Using a method reference to call a static method.
import java.util.function.Function;
public class MethodReferenceExample {
public static void main(String[] args) {
Function<String, Integer> parseIntFunction = Integer::parseInt;
int value = parseIntFunction.apply("42");
System.out.println(value);
}
}
Output: 42
In this example, we create a Function
that takes a String
argument and returns an Integer
. We use a method reference Integer::parseInt
to call the static method parseInt
on the Integer
class, which takes a String
argument and returns an int
. When we call the apply
method on the parseIntFunction
with the argument “42”, the parseInt
method is called with the argument “42” and returns the integer value 42, which is then returned by the apply
method and assigned to the value
variable. Finally, we print the value
variable to the console.
Example 2: Using a method reference to call an instance method on an object.
import java.util.Arrays;
import java.util.List;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println);
}
}
Output:
Alice
Bob
Charlie
In this example, we create a list of strings names
and use a method reference System.out::println
with the forEach
method to call the println
method on the System.out
object for each element of the list. This is equivalent to using a lambda expression name -> System.out.println(name)
as we did in the previous example, but using a method reference can make the code more concise and easier to read.
Example 3: Using a method reference to call a constructor.
import java.util.function.Supplier;
public class MethodReferenceExample {
public static void main(String[] args) {
Supplier<StringBuilder> stringBuilderSupplier = StringBuilder::new;
StringBuilder sb = stringBuilderSupplier.get();
sb.append("Hello, ");
sb.append("World!");
System.out.println(sb.toString());
}
}
Output: Hello, World!
In this example, we create a Supplier
that takes no arguments and returns a new StringBuilder
object by using the constructor reference StringBuilder::new
. When we call the get
method on the stringBuilderSupplier
, a new StringBuilder
object is created and returned, which we then use to append the strings “Hello, ” and “World!” and print the result to the console.
Lambda Expression as the User-defined Method References
In addition to using built-in methods in method references, we can also use user-defined methods. In this section, we will explore several examples of using user-defined method references with lambda expressions.
Example 1: Using a user-defined method reference to call an instance method.
import java.util.function.Consumer;
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("Hello, my name is " + name);
}
}
public class MethodReferenceExample {
public static void main(String[] args) {
Person person = new Person("Alice");
Consumer<Person> sayHelloConsumer = Person::sayHello;
sayHelloConsumer.accept(person);
}
}
Output: Hello, my name is Alice
In this example, we create a Person
class with a sayHello
method that prints a greeting to the console. We create a Consumer
that takes a Person
argument and calls the sayHello
method on it. We use a user-defined method reference Person::sayHello
to refer to the sayHello
method of the Person
class. When we call the accept
method on the sayHelloConsumer
with the person
object as an argument, the sayHello
method is called on the person
object, resulting in the output Hello, my name is Alice
.
Example 2: Using a user-defined method reference to call a static method.
import java.util.function.Function;
class StringUtil {
public static String reverse(String s) {
StringBuilder sb = new StringBuilder(s);
return sb.reverse().toString();
}
}
public class MethodReferenceExample {
public static void main(String[] args) {
Function<String, String> reverseFunction = StringUtil::reverse;
String reversed = reverseFunction.apply("Hello, World!");
System.out.println(reversed);
}
}
Output: !dlroW ,olleH
In this example, we create a StringUtil
class with a static reverse
method that takes a String
argument and returns the reversed String
. We create a Function
that takes a String
argument and returns a String
. We use a user-defined method reference StringUtil::reverse
to refer to the reverse
method of the StringUtil
class. When we call the apply
method on the reverseFunction
with the argument “Hello, World!”, the reverse
method is called on the StringUtil
class with the argument “Hello, World!”, resulting in the output !dlroW ,olleH
.
Example 3: Using a user-defined method reference with multiple arguments.
import java.util.function.BiFunction;
class Calculator {
public static int add(int a, int b) {
return a + b;
}
}
public class MethodReferenceExample {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> addFunction = Calculator::add;
int result = addFunction.apply(3, 5);
System.out.println(result);
}
}
Output: 8
In this example, we create a Calculator
class with a static add
method that takes two int
arguments and returns their sum. We create a BiFunction
that takes two Integer
arguments and returns an Integer
. We use a user-defined method reference Calculator::add
to refer to the add
method of the Calculator
class. When we call the apply
method on the addFunction
with the arguments 3 and 5, the add
method is called on the Calculator
class with the arguments 3 and 5, resulting in the output 8
.
Example 4: Using a user-defined method reference with a constructor.
import java.util.function.Function;
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class MethodReferenceExample {
public static void main(String[] args) {
Function<String, Person> personConstructor = Person::new;
Person person = personConstructor.apply("Alice");
System.out.println(person.getName());
}
}
Output: Alice
In this example, we create a Person
class with a constructor that takes a String
argument and sets the name
instance variable. We create a Function
that takes a String
argument and returns a Person
object. We use a user-defined method reference Person::new
to refer to the constructor of the Person
class. When we call the apply
method on the personConstructor
with the argument “Alice”, a new Person
object is created with the name
instance variable set to “Alice”, resulting in the output Alice
.
Example 5: Using a user-defined method reference with an instance method and a parameter.
import java.util.function.Function;
class StringUtil {
private int multiplier;
public StringUtil(int multiplier) {
this.multiplier = multiplier;
}
public String multiply(String s) {
StringBuilder sb = new StringBuilder(s);
for (int i = 0; i < multiplier - 1; i++) {
sb.append(s);
}
return sb.toString();
}
}
public class MethodReferenceExample {
public static void main(String[] args) {
StringUtil stringUtil = new StringUtil(3);
Function<String, String> multiplyFunction = stringUtil::multiply;
String result = multiplyFunction.apply("hello");
System.out.println(result);
}
}
Output: hellohellohello
In this example, we create a StringUtil
class with an instance method multiply
that takes a String
argument and multiplies it by the multiplier
instance variable. We create a Function
that takes a String
argument and returns a String
. We create a StringUtil
object with a multiplier
value of 3. We use a user-defined method reference stringUtil::multiply
to refer to the multiply
instance method of the StringUtil
object. When we call the apply
method on the multiplyFunction
with the argument “hello”, the multiply
method is called on the stringUtil
object with the argument “hello”, resulting in the output hellohellohello
.
Conclusion
In conclusion, lambda expressions and method references are powerful features of Java that allow us to write more concise and expressive code. They can be used in various contexts, including functional interfaces, collections, and user-defined methods. By using lambda expressions and method references, we can write code that is easier to read, write, and maintain.
Also, see the example code JavaExamples_NoteArena in our GitHub repository. See complete examples in our GitHub repositories.
Follow us on social media
Follow Author