Biểu thức Java Lambda (Có ví dụ)

Trong bài viết này, chúng ta sẽ tìm hiểu về biểu thức lambda trong Java và cách sử dụng biểu thức lambda với các giao diện chức năng, giao diện chức năng chung và API luồng với sự trợ giúp của các ví dụ.

Biểu thức lambda được giới thiệu lần đầu tiên trong Java 8. Mục tiêu chính của nó là tăng sức mạnh biểu đạt của ngôn ngữ.

Tuy nhiên, trước khi đi vào lambdas, trước tiên chúng ta cần hiểu các giao diện chức năng.

Giao diện chức năng là gì?

Nếu một giao diện Java chứa một và chỉ một phương thức trừu tượng thì nó được gọi là giao diện chức năng. Chỉ một phương pháp này xác định mục đích dự kiến ​​của giao diện.

Ví dụ, Runnablegiao diện từ gói java.lang; là một giao diện chức năng vì nó chỉ tạo thành một phương thức tức là run().

Ví dụ 1: Xác định giao diện chức năng trong java

 import java.lang.FunctionalInterface; @FunctionalInterface public interface MyInterface( // the single abstract method double getValue(); )

Trong ví dụ trên, giao diện MyInterface chỉ có một phương thức trừu tượng getValue (). Do đó, nó là một giao diện chức năng.

Ở đây, chúng tôi đã sử dụng chú thích @FunctionalInterface. Chú thích buộc trình biên dịch Java chỉ ra rằng giao diện là một giao diện chức năng. Do đó, không cho phép có nhiều hơn một phương thức trừu tượng. Tuy nhiên, nó không phải là bắt buộc.

Trong Java 7, các giao diện chức năng được coi là các Phương thức Tóm tắt Đơn hoặc kiểu SAM . SAM thường được triển khai với Lớp ẩn danh trong Java 7.

Ví dụ 2: Triển khai SAM với các lớp ẩn danh trong java

 public class FunctionInterfaceTest ( public static void main(String() args) ( // anonymous class new Thread(new Runnable() ( @Override public void run() ( System.out.println("I just implemented the Runnable Functional Interface."); ) )).start(); ) )

Đầu ra :

 Tôi vừa triển khai Giao diện chức năng Runnable.

Ở đây, chúng ta có thể chuyển một lớp ẩn danh cho một phương thức. Điều này giúp viết các chương trình với ít mã hơn trong Java 7. Tuy nhiên, cú pháp vẫn còn khó khăn và cần nhiều dòng mã bổ sung.

Java 8 đã mở rộng sức mạnh của SAMs bằng cách tiến thêm một bước nữa. Vì chúng ta biết rằng một giao diện chức năng chỉ có một phương thức, nên không cần xác định tên của phương thức đó khi chuyển nó làm đối số. Biểu thức Lambda cho phép chúng tôi làm chính xác điều đó.

Giới thiệu về biểu thức lambda

Về cơ bản, biểu thức Lambda là một phương thức ẩn danh hoặc không được đặt tên. Biểu thức lambda không tự thực thi. Thay vào đó, nó được sử dụng để triển khai một phương thức được xác định bởi một giao diện chức năng.

Làm thế nào để xác định biểu thức lambda trong Java?

Đây là cách chúng ta có thể định nghĩa biểu thức lambda trong Java.

 (parameter list) -> lambda body

Toán tử mới ( ->) được sử dụng được gọi là toán tử mũi tên hoặc toán tử lambda. Cú pháp có thể không rõ ràng vào lúc này. Hãy cùng khám phá một số ví dụ,

Giả sử, chúng ta có một phương thức như sau:

 double getPiValue() ( return 3.1415; )

Chúng ta có thể viết phương thức này bằng cách sử dụng biểu thức lambda như sau:

 () -> 3.1415

Ở đây, phương thức không có bất kỳ tham số nào. Do đó, phía bên trái của toán tử bao gồm một tham số trống. Phía bên phải là phần thân lambda chỉ định hành động của biểu thức lambda. Trong trường hợp này, nó trả về giá trị 3,1415.

Các loại Lambda Body

Trong Java, thân lambda có hai loại.

1. Một nội dung với một biểu thức duy nhất

 () -> System.out.println("Lambdas are great");

Loại cơ thể lambda này được gọi là cơ thể biểu hiện.

2. Một phần thân bao gồm một khối mã.

 () -> ( double pi = 3.1415; return pi; );

Loại thân lambda này được gọi là thân khối. Phần thân khối cho phép phần thân lambda bao gồm nhiều câu lệnh. Những câu lệnh này được đặt bên trong dấu ngoặc nhọn và bạn phải thêm dấu chấm phẩy sau dấu ngoặc nhọn.

Lưu ý : Đối với phần thân khối, bạn có thể có câu lệnh trả về nếu phần thân trả về một giá trị. Tuy nhiên, phần thân của biểu thức không yêu cầu câu lệnh trả về.

Ví dụ 3: Biểu thức Lambda

Hãy viết một chương trình Java trả về giá trị của Pi bằng cách sử dụng biểu thức lambda.

Như đã đề cập trước đó, một biểu thức lambda không được thực thi riêng. Đúng hơn, nó hình thành việc triển khai phương thức trừu tượng được xác định bởi giao diện chức năng.

Vì vậy, trước tiên chúng ta cần xác định một giao diện chức năng.

 import java.lang.FunctionalInterface; // this is functional interface @FunctionalInterface interface MyInterface( // abstract method double getPiValue(); ) public class Main ( public static void main( String() args ) ( // declare a reference to MyInterface MyInterface ref; // lambda expression ref = () -> 3.1415; System.out.println("Value of Pi = " + ref.getPiValue()); ) )

Đầu ra :

 Giá trị của Pi = 3,1415

Trong ví dụ trên,

  • Chúng tôi đã tạo một giao diện chức năng có tên MyInterface. Nó chứa một phương thức trừu tượng duy nhất có têngetPiValue()
  • Bên trong lớp Chính, chúng ta đã khai báo một tham chiếu đến MyInterface. Lưu ý rằng chúng ta có thể khai báo một tham chiếu của một giao diện nhưng chúng ta không thể khởi tạo một giao diện. Đó là,
     // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
  • Sau đó, chúng tôi đã gán một biểu thức lambda cho tham chiếu.
     ref = () -> 3.1415;
  • Cuối cùng, chúng tôi gọi phương thức getPiValue()bằng giao diện tham chiếu. Khi nào
     System.out.println("Value of Pi = " + ref.getPiValue());

Biểu thức Lambda với các tham số

Cho đến bây giờ chúng ta đã tạo các biểu thức lambda mà không có bất kỳ tham số nào. Tuy nhiên, tương tự như các phương thức, biểu thức lambda cũng có thể có các tham số. Ví dụ,

 (n) -> (n%2)==0

Here, the variable n inside the parenthesis is a parameter passed to the lambda expression. The lambda body takes the parameter and checks if it is even or odd.

Example 4: Using lambda expression with parameters

 @FunctionalInterface interface MyInterface ( // abstract method String reverse(String n); ) public class Main ( public static void main( String() args ) ( // declare a reference to MyInterface // assign a lambda expression to the reference MyInterface ref = (str) -> ( String result = ""; for (int i = str.length()-1; i>= 0 ; i--) result += str.charAt(i); return result; ); // call the method of the interface System.out.println("Lambda reversed = " + ref.reverse("Lambda")); ) )

Output:

 Lambda reversed = adbmaL

Generic Functional Interface

Till now we have used the functional interface that accepts only one type of value. For example,

 @FunctionalInterface interface MyInterface ( String reverseString(String n); )

The above functional interface only accepts String and returns String. However, we can make the functional interface generic, so that any data type is accepted. If you are not sure about generics, visit Java Generics.

Example 5: Generic Functional Interface and Lambda Expressions

 // GenericInterface.java @FunctionalInterface interface GenericInterface ( // generic method T func(T t); ) // GenericLambda.java public class Main ( public static void main( String() args ) ( // declare a reference to GenericInterface // the GenericInterface operates on String data // assign a lambda expression to it GenericInterface reverse = (str) -> ( String result = ""; for (int i = str.length()-1; i>= 0 ; i--) result += str.charAt(i); return result; ); System.out.println("Lambda reversed = " + reverse.func("Lambda")); // declare another reference to GenericInterface // the GenericInterface operates on Integer data // assign a lambda expression to it GenericInterface factorial = (n) -> ( int result = 1; for (int i = 1; i <= n; i++) result = i * result; return result; ); System.out.println("factorial of 5 = " + factorial.func(5)); ) )

Output:

 Lambda reversed = adbmaL factorial of 5 = 120

In the above example, we have created a generic functional interface named GenericInterface. It contains a generic method named func().

Here, inside the Main class,

  • GenericInterface reverse - creates a reference to the interface. The interface now operates on String type of data.
  • GenericInterface factorial - creates a reference to the interface. The interface, in this case, operates on the Integer type of data.

Lambda Expression and Stream API

The new java.util.stream package has been added to JDK8 which allows java developers to perform operations like search, filter, map, reduce, or manipulate collections like Lists.

For example, we have a stream of data (in our case a List of String) where each string is a combination of country name and place of the country. Now, we can process this stream of data and retrieve only the places from Nepal.

For this, we can perform bulk operations in the stream by the combination of Stream API and Lambda expression.

Example 6: Demonstration of using lambdas with the Stream API

 import java.util.ArrayList; import java.util.List; public class StreamMain ( // create an object of list using ArrayList static List places = new ArrayList(); // preparing our data public static List getPlaces()( // add places and country to the list places.add("Nepal, Kathmandu"); places.add("Nepal, Pokhara"); places.add("India, Delhi"); places.add("USA, New York"); places.add("Africa, Nigeria"); return places; ) public static void main( String() args ) ( List myPlaces = getPlaces(); System.out.println("Places from Nepal:"); // Filter places from Nepal myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p)); ) )

Output:

 Places from Nepal: NEPAL, KATHMANDU NEPAL, POKHARA

In the above example, notice the statement,

 myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p));

Here, we are using the methods like filter(), map() and forEach() of the Stream API. These methods can take a lambda expression as input.

Chúng ta cũng có thể xác định các biểu thức của riêng mình dựa trên cú pháp mà chúng ta đã học ở trên. Điều này cho phép chúng ta giảm đáng kể các dòng mã như chúng ta đã thấy trong ví dụ trên.

thú vị bài viết...