Lambda là một tính năng trong ngôn ngữ lập trình Java cho phép định nghĩa và sử dụng các hàm ẩn danh một cách tiện lợi. Lambda giúp viết mã ngắn gọn hơn và tập trung vào logic xử lý chính mà không cần phải tạo ra các phần code phức tạp.
- Ví dụ khi KHÔNG SỬ DỤNG LAMBDA
public class noLambdaExample {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Welcome to JAVA!");
}
});
thread.start();
}
}
> Output: Welcome to JAVA!
- Ví dụ khi SỬ DỤNG LAMBDA
public class LambdaExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Welcome to JAVA PROGRAM!");
});
thread.start();
}
}
> Output: Welcome to JAVA PROGRAM!
- Lambda không có tham số:
() -> { // code };
- Lambda có một tham số
(parameter) -> { // code };
- Lambda với nhiều tham số
(parameter1, parameter2) -> { // code };
- Lambda không có cặp ngoặc và kiểu dữ liệu tự suy luận
parameter -> { }; // code ;
- Lambda trong một biểu thức
(parameter) -> expression;
- Lambda với các phương thức chuẩn
(parameter) -> method(parameter);
- Lambda với các phương thức tĩnh
(parameter) -> Class.method(parameter);
- Lambda với điều kiện (filtering)
(parameter) -> expression that returns boolean;
- Lambda với các phương thức non-static
(parameter) -> object.method(parameter);
- Lambda với các constructor
(parameter) -> new ClassName(parameter);
Example: package
Lambda với Class
LambdaExpression cùng với Interface
myFunctionInterF
package Lambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class LambdaExpression implements myFunctionInterF {
public static void main(String[] args) {
//** 1. Lambda không có tham số
MyFunctionalInterface1 func1 = () ->
System.out.println("Hello World");
System.out.print("Không có tham số: ");
func1.myMethod1();
//! 2. Lambda có 1 tham số
MyFunctionalInterface2 func2 = (x) -> {return x * 2;};
int result2 = func2.myMethod2(5);
System.out.println("Có 1 tham số (1): " + result2);
//? 3. Lambda có nhiều tham số
MyFunctionalInterface3 func3 = (x,y) -> {
return x+y;
};
int result3 = func3.myMethod3(5, 6);
System.out.println("Có nhiều tham số (2): " + result3);
//? 4. Lambda không có cặp ngoặc hoặc kiểu dữ liệu tự suy luận
MyFunctionalInterface4 func4 = x -> x*x;
int result4 = func4.myMethod4(3);
System.out.println("Không có cặp ngoặc và kiểu dữ liệu tự suy luận (4): " + result4 );
//? 5. Lambda trong 1 biểu thức
MyFunctionalInterface5 func5 = (x) -> x * x;
int result5 = func5.myMethod5(3);
System.out.println("Lambda trong 1 biểu thức (5): " + result5);
//? 6. Lamda với phương thức chuẩn
MyFunctionalInterface6 func6 = x -> System.out.println(x);
func6.myMethod6("Lambda với phương thức chuẩn! -> (6)");
//? 7. Lambda với phương thức tĩnh
MyFunctionalInterface7 func7 = x -> Math.abs(x);
int result7 = func7.myMethod7(-5);
System.out.println("Lambda với phương thức tĩnh (7): " + result7);
//? 8. Lambda với điều kiệu (Filtering - BOOLEAN)
MyFunctionalInterface8 func8 = x -> x > 5;
boolean result8 = func8.myMethod8(6);
System.out.println("Lambda với điều kiệu (Filtering - BOOLEAN) (8): " + result8);
//? 9. Lambda với các phương thức non-static
String str = "Hello Lambda";
MyFunctionalInterface9 func9 = s -> s.length();
int result9 = func9.myMethod9(str);
System.out.println("Lambda với các phương thức non-static (9): " + result9);
//? 10. Lambda với các constructor
MyFunctionalInterface10 func10 = x -> new ArrayList<>(Arrays.asList(x));
List<Integer> result10 = func10.myMethod10(new Integer[]{1,3,5,7,9,11,13,15,17});
System.out.println("Lambda với các constructor (10): " + result10);
}
}
// Output:
Không có tham số (1): Hello World
Có 1 tham số (2): 10
Có nhiều tham số (3): 11
Không có cặp ngoặc và kiểu dữ liệu tự suy luận (4): 9
Lambda trong 1 biểu thức (5): 9
Lambda với phương thức chuẩn! -> (6)
Lambda với phương thức tĩnh (7): 5
Lambda với điều kiệu (Filtering - BOOLEAN) (8): true
Lambda với các phương thức non-static (9): 12
Lambda với các constructor (10): [1, 3, 5, 7, 9, 11, 13, 15, 17]
- Lambda đối tượng (Object Lambdas):
- Lambda đối tượng triển khai một giao diện chức năng và được lưu trữ trong một biến để sử dụng sau này.
Ví dụ, khi chúng ta gán một biểu thức lambda vào một biến để sau đó sử dụng.
Example ObjectLambda:
package ObjectInlinesLambda;
public class ObjectLambda {
public static void main(String[] args) {
Calculator calc = (a,b) -> a+b;
int resultOL = calc.add(6, 4);
System.out.println("Result ObjectLambda: " + resultOL);
}
}
- Lambda trực tiếp (Inline Lambdas):
- Lambda trực tiếp được truyền trực tiếp như tham số cho các phương thức.
Ví dụ, khi chúng ta truyền một biểu thức lambda vào một phương thức ngay lập tức.
Example InlineLambdas:
package ObjectInlinesLambda;
public class InlineLambdas {
public static void main(String[] args) {
performanceOperation((a,b) -> a*b,7,4);
}
static void performanceOperation(Calculator calc2, int x, int y){
int resultIL = calc2.add(x, y);
System.out.println("Result Inline Lambda: " + resultIL);
}
}
Cú pháp biến cục bộ cho tham số lambda (Local Variable Syntax) là một tính năng trong Java 10 trở lên. Nó cho phép bạn khai báo tham số của một biểu thức lambda mà không cần phải xác định kiểu dữ liệu của biến đó. Thay vì viết:
(String s) -> { ... }
bạn có thể viết:
var s -> { ... }
Điều này giúp làm cho mã nguồn ngắn gọn hơn và dễ đọc hơn.
Example VariableSyntax
package ObjectInlinesLambda;
public class VariableSyntax {
public static void main(String[] args) {
Calculator vs = (var x, var y) -> x + y;
int result = vs.add(10, 20);
System.out.println("Result: " + result);
}
}
Result: 30
-- More...: Class
GENERIC LAMBDA && Interface
Calculator
-
Package : java.util.
function
là một package nằm trong thư viện chuẩn của gói Java. -
java.util.
function
cung cấp các kiểu đích cho các biểu thức lambda và tham chiếu phương thức (Method Reference). -
java.util.
function
có hơn 43 loạiFunctional Inteface
.
Tuy nhiên trong đó có 4 loại đặc trưng , thường được sử dụng nhiều nhất cho Lambda Expression.
Predicate<T>
Consumer<T>
Function<T,R>
Supplier<T>
Example: java.util.
function
Predicate<T>
trong Package : java.util.function
đại diện cho một hoạt động kiểm tra điều kiện và trả về một giá trị boolean như kết quả. Nó thường được sử dụng để kiểm tra các điều kiện trong các hoạt động như lọc dữ liệu theo kiểu <T>
.
Abstract Method được áp dụng nhiều là
// Abstract Method
boolean test(T t)
Phương thức này được sử dụng để đánh giá điều kiện (predicate) trên một đối tượng đầu vào (t). Nó kiểm tra xem đối tượng đầu vào có thỏa mãn điều kiện được định nghĩa trong Predicate hay không.
Predicate<Integer> isPositive = (num) -> num > 0;
System.out.println(isPositive.test(5)); // Output: true
System.out.println(isPositive.test(-5)); // Output: false
List<String> names = Arrays.asList("John", "Jane", "Steve", "Mary");
names.stream().filter(name -> name.startsWith("J")).forEach(System.out::println);
// Output: John, Jane
Consumer<T>
Giao diện Consumer đại diện cho một hoạt động nhận đối số nhưng không trả về gì cả. Nó thường được sử dụng để tiêu thụ (consume) dữ liệu hoặc thực hiện một hành động với đối tượng đó.
// Abstract Method
void accept(T t);
Example:
Consumer<List<String>> printList = (list) -> list.forEach(System.out::println);
List<String> colors = Arrays.asList("Red", "Green", "Blue");
printList.accept(colors);
// Output: Red, Green, Blue
List<String> names = Arrays.asList("John", "Jane", "Steve", "Mary");
names.forEach(name -> {
Consumer<String> printGreeting = (str) -> System.out.println("Hello, " + str);
printGreeting.accept(name);
});
// Output:
// Hello, John
// Hello, Jane
// Hello, Steve
// Hello, Mary
// Consumer with a Stream:
List<String> colors = Arrays.asList("Red", "Green", "Blue");
colors.stream().forEach(color -> {
Consumer<String> printColor = (str) -> System.out.println("Color: " + str);
printColor.accept(color);
});
// Output:
// Color: Red
// Color: Green
// Color: Blue
Function<T,R>
Giao diện Function đại diện cho một hoạt động nhận đối số và trả về một kết quả cho người gọi. Nó thường được sử dụng để thực hiện các phép biến đổi hoặc tính toán trên dữ liệu.
// Abstract Method
R apply(T t)
Function<String, Integer> strLength = (str) -> str.length();
int length = strLength.apply("OpenAI");
System.out.println("Length: " + length); // Output: Length: 6
Function<Integer, Boolean> isEven = (num) -> num % 2 == 0;
System.out.println(isEven.apply(3)); // Output: false
System.out.println(isEven.apply(4)); // Output: true
Supplier<T>
Giao diện Supplier đại diện cho một hoạt động không nhận bất kỳ đối số nào, nhưng trả về một giá trị cho người gọi. Nó thường được sử dụng để cung cấp (supply) dữ liệu hoặc kết quả cho các phần khác của chương trình.
// Abstract Method
T get()
Supplier<String> getGreeting = () -> "Hello, World!";
System.out.println(getGreeting.get()); // Output: Hello, World!
Supplier<LocalDate> getCurrentDate = LocalDate::now;
System.out.println("Current date: " + getCurrentDate.get()); // Output: Current date: [current date]
Supplier<List<Integer>> getNumbers = () -> Arrays.asList(1, 2, 3, 4, 5);
System.out.println(getNumbers.get()); // Output: [1, 2, 3, 4, 5]
-
Functional Interfaces và Loại Dữ Liệu Áp Dụng:
- Các giao diện chức năng (
Predicate<T>
,Consumer<T>
,Function<T, R>
, vàSupplier<T>
) được thiết kế để thực hiện các hoạt động khác nhau trên đối tượng của loại tham chiếu (Reference Type Objects
).
- Các giao diện chức năng (
-
Dữ Liệu Nguyên Thuỷ không được áp Dụng:
- Các giá trị nguyên thuỷ như
int, long, float
, hoặcdouble
không thể được sử dụng trực tiếp với các giao diện chức năng này.
- Các giá trị nguyên thuỷ như
-
Phiên Bản Nguyên Thuỷ:
- Do đó, Java cung cấp các phiên bản nguyên thuỷ cho các giao diện chức năng này. Ví dụ,
IntPredicate
,LongPredicate
, vàDoublePredicate
là các phiên bản nguyên thuỷ của giao diệnPredicate
. Tương tự,IntConsumer
,LongConsumer
, vàDoubleConsumer
là các phiên bản nguyên thuỷ của giao diệnConsumer
.
- Do đó, Java cung cấp các phiên bản nguyên thuỷ cho các giao diện chức năng này. Ví dụ,
- Example:
java.util.function.(*)
import java.util.function.*;
public class Main {
public static void main(String[] args) {
// IntPredicate
IntPredicate isEven = (num) -> num % 2 == 0;
System.out.println(isEven.test(4)); // Output: true
System.out.println(isEven.test(7)); // Output: false
// LongPredicate
LongPredicate isPositive = (num) -> num > 0;
System.out.println(isPositive.test(10L)); // Output: true
System.out.println(isPositive.test(-5L)); // Output: false
// DoublePredicate
DoublePredicate isGreaterThanZero = (num) -> num > 0.0;
System.out.println(isGreaterThanZero.test(3.5)); // Output: true
System.out.println(isGreaterThanZero.test(-2.0)); // Output: false
}
}
BiPredicate
,BiConsumer
, vàBiFunction
là các biến thể của giao diện Predicate, Consumer, và Function tương ứng. Điểm khác biệt chính nằm ở số lượng đối số mà chúng có thể chấp nhận.
BiPredicate<Integer, String> isLengthEqual = (num, str) -> str.length() == num;
System.out.println(isLengthEqual.test(5, "Hello")); // Output: true
System.out.println(isLengthEqual.test(5, "World")); // Output: false
Example:
BiPredicate
,BiConsumer
,BiFunction
import java.util.function.BiPredicate;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
public class biPre {
public static void main(String[] args) {
// BiPredicate example
BiPredicate<Integer, String> isLengthEqual = (num, str) -> str.length() == num;
System.out.println(isLengthEqual.test(5, "Hello")); // Output: true
System.out.println(isLengthEqual.test(5, "World")); // Output: false
// BiConsumer example
BiConsumer<String, Integer> printKeyValue = (key, value) -> System.out.println(key + " : " + value);
printKeyValue.accept("Age", 30); // Output: Age : 30
// BiFunction example
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
System.out.println(add.apply(5, 3)); // Output: 8
}
}
// Output:
true
true
Age : 30
8
BiPredicate
- BiConsumer
- BiFunction
Example: BiFunction
// BiPredicate
BiPredicate<T, U>
// BiConsumer
BiConsumer<T, U>
// BiFunction
BiFunction<T, U, R>
- Đây là một functional interface định nghĩa trong java.util.function package.
- Nó là một phiên bản đặc biệt của
Function<T, T>
trong đó cả kiểu dữ liệu đầu vào và đầu ra đều là cùng một kiểu T. UnaryOperator
thích hợp khi bạn muốn thực hiện một phép toán trên một đối tượng và kết quả là cùng kiểu dữ liệu.
import java.util.function.UnaryOperator;
public class UnaryOperatorExample {
public static void main(String[] args) {
// Định nghĩa một UnaryOperator để nhân một số nguyên cho 2
UnaryOperator<Integer> multiplyByTwo = x -> x * 2;
// Áp dụng phép toán lên một số
int result = multiplyByTwo.apply(5);
System.out.println("Result: " + result); // Output: Result: 10
}
}
// Result: 10