Biểu thức Java Lambda

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: Định nghĩa 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à một phương pháp trừu tượng đơ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 :

I just implemented the Runnable Functional Interface.

Ở đâ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 phải xác định tên của phương thức đó khi chuyển nó như một đối số. Biểu thức Lambda cho phép chúng tôi thực hiện 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ể xác định 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 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. Thay vào đó, 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 :

Value of Pi = 3.1415

Trong ví dụ trên,

  • We have created a functional interface named MyInterface. It contains a single abstract method named getPiValue()
  • Inside the Main class, we have declared a reference to MyInterface. Note that we can declare a reference of an interface but we cannot instantiate an interface. That is,
     // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
  • We then assigned a lambda expression to the reference.ref = () -> 3.1415;
  • Finally, we call the method getPiValue() using the reference interface. When
     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

Ở đây, biến n bên trong dấu ngoặc đơn là một tham số được truyền cho biểu thức lambda. Phần thân lambda nhận tham số và kiểm tra xem nó là chẵn hay lẻ.

Ví dụ 4: Sử dụng biểu thức lambda với các tham số

@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"));
    }

}

Đầu ra :

Lambda reversed = adbmaL

Giao diện chức năng chung

Cho đến bây giờ chúng ta đã sử dụng giao diện chức năng chỉ chấp nhận một loại giá trị. Ví dụ,

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

Giao diện chức năng trên chỉ chấp nhận Stringvà trả về String. Tuy nhiên, chúng ta có thể làm cho giao diện chức năng chung chung, để bất kỳ kiểu dữ liệu nào cũng được chấp nhận. Nếu bạn không chắc chắn về generic, hãy truy cập Java Generics .

Ví dụ 5: Giao diện chức năng chung và biểu thức Lambda

// GenericInterface.java
@FunctionalInterface
interface GenericInterface<T> {

    // 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<String> 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<Integer> 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));
    }
}

Đầu ra :

Lambda reversed = adbmaL
factorial of 5 = 120

Trong ví dụ trên, chúng ta đã tạo một giao diện chức năng chung có tên là GenericInterface . Nó chứa một phương thức chung được đặt tên func().

Đây, bên trong lớp Chính,

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

Lambda Expression and Stream API

Gói java.util.stream mới đã được thêm vào JDK8, cho phép các nhà phát triển java thực hiện các thao tác như tìm kiếm, lọc, ánh xạ, thu nhỏ hoặc thao tác các bộ sưu tập như Lists.

Ví dụ: chúng tôi có một luồng dữ liệu (trong trường hợp của chúng tôi là a Listlà String) trong đó mỗi chuỗi là sự kết hợp của tên quốc gia và địa danh của quốc gia. Giờ đây, chúng tôi có thể xử lý luồng dữ liệu này và chỉ lấy các địa điểm từ Nepal.

Đối với điều này, chúng tôi có thể thực hiện các hoạt động hàng loạt trong luồng bằng sự kết hợp của API luồng và biểu thức Lambda.

Ví dụ 6: Trình diễn việc sử dụng lambdas với Stream API

import java.util.ArrayList;
import java.util.List;

public class StreamMain {

    // create an object of list using ArrayList
    static List<String> 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<String> 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));
    }

}

Đầu ra :

Places from Nepal:
NEPAL, KATHMANDU
NEPAL, POKHARA

Trong ví dụ trên, hãy chú ý câu lệnh,

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

Ở đây, chúng ta đang sử dụng các phương pháp như filter()map()và forEach()các API Stream. Các phương thức này có thể nhận một biểu thức lambda làm đầu vào.

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 bớt các dòng mã một cách đáng kể như chúng ta đã thấy trong ví dụ trên.









Gõ tìm kiếm nhanh...