CodeGym /Blog Java /Ngẫu nhiên /Giải thích về các biểu thức lambda trong Java. Với các ví...
John Squirrels
Mức độ
San Francisco

Giải thích về các biểu thức lambda trong Java. Với các ví dụ và nhiệm vụ. Phần 1

Xuất bản trong nhóm
Bài viết này dành cho ai?
  • Nó dành cho những người nghĩ rằng họ đã biết rõ về Java Core, nhưng không biết gì về các biểu thức lambda trong Java. Hoặc có thể họ đã nghe điều gì đó về biểu thức lambda, nhưng thiếu thông tin chi tiết
  • Nó dành cho những người có hiểu biết nhất định về các biểu thức lambda, nhưng vẫn cảm thấy khó khăn và không quen sử dụng chúng.
Giải thích về các biểu thức lambda trong Java.  Với các ví dụ và nhiệm vụ.  Phần 1 - 1Nếu bạn không phù hợp với một trong những loại này, bạn có thể thấy bài viết này nhàm chán, thiếu sót hoặc nói chung không phải là tách trà của bạn. Trong trường hợp này, vui lòng chuyển sang chủ đề khác hoặc nếu bạn thông thạo về chủ đề này, vui lòng đưa ra đề xuất trong phần nhận xét về cách tôi có thể cải thiện hoặc bổ sung bài viết. Tài liệu không tuyên bố là có bất kỳ giá trị học thuật nào, chứ chưa nói đến tính mới. Hoàn toàn ngược lại: Tôi sẽ cố gắng mô tả những thứ phức tạp (đối với một số người) một cách đơn giản nhất có thể. Yêu cầu giải thích về Stream API đã thôi thúc tôi viết bài này. Tôi đã nghĩ về điều đó và quyết định rằng một số ví dụ về luồng của tôi sẽ không thể hiểu được nếu không hiểu các biểu thức lambda. Vì vậy, chúng ta sẽ bắt đầu với các biểu thức lambda. Bạn cần biết gì để hiểu bài viết này?
  1. Bạn nên hiểu lập trình hướng đối tượng (OOP), cụ thể là:

    • lớp, đối tượng và sự khác biệt giữa chúng;
    • giao diện, chúng khác với lớp như thế nào và mối quan hệ giữa giao diện và lớp;
    • các phương thức, cách gọi chúng, các phương thức trừu tượng (tức là các phương thức không có triển khai), tham số của phương thức, đối số của phương thức và cách truyền chúng;
    • công cụ sửa đổi truy cập, phương thức/biến tĩnh, phương thức/biến cuối cùng;
    • kế thừa các lớp và giao diện, đa kế thừa giao diện.
  2. Kiến thức về Java Core: kiểu chung (generics), tập hợp (list), luồng.
Vâng, chúng ta hãy nhận được nó.

Một ít lịch sử

Các biểu thức Lambda đến với Java từ lập trình chức năng và sau đó là toán học. Tại Hoa Kỳ vào giữa thế kỷ 20, Alonzo Church, người rất yêu thích toán học và tất cả các loại trừu tượng, đã làm việc tại Đại học Princeton. Chính Alonzo Church là người đã phát minh ra phép tính lambda, ban đầu là một tập hợp các ý tưởng trừu tượng hoàn toàn không liên quan đến lập trình. Các nhà toán học như Alan Turing và John von Neumann làm việc cùng lúc tại Đại học Princeton. Mọi thứ kết hợp với nhau: Church đã nghĩ ra phép tính lambda. Turing đã phát triển máy tính trừu tượng của mình, hiện được gọi là "máy Turing". Và von Neumann đã đề xuất một kiến ​​trúc máy tính đã hình thành nền tảng của các máy tính hiện đại (nay được gọi là "kiến trúc von Neumann"). Vào thời điểm đó, Nhà thờ Alonzo' ý tưởng của ông đã không trở nên nổi tiếng như các công trình của các đồng nghiệp của ông (ngoại trừ lĩnh vực toán học thuần túy). Tuy nhiên, một thời gian sau, John McCarthy (cũng là sinh viên tốt nghiệp Đại học Princeton và vào thời điểm chúng tôi kể câu chuyện, là nhân viên của Viện Công nghệ Massachusetts) bắt đầu quan tâm đến ý tưởng của Church. Năm 1958, ông đã tạo ra ngôn ngữ lập trình chức năng đầu tiên, LISP, dựa trên những ý tưởng đó. Và 58 năm sau, ý tưởng về lập trình hàm rò rỉ vào Java 8. Chưa đầy 70 năm đã trôi qua... Thành thật mà nói, đây không phải là khoảng thời gian dài nhất để một ý tưởng toán học được áp dụng vào thực tế. một nhân viên của Viện Công nghệ Massachusetts) bắt đầu quan tâm đến các ý tưởng của Church. Năm 1958, ông đã tạo ra ngôn ngữ lập trình chức năng đầu tiên, LISP, dựa trên những ý tưởng đó. Và 58 năm sau, ý tưởng về lập trình hàm rò rỉ vào Java 8. Chưa đầy 70 năm đã trôi qua... Thành thật mà nói, đây không phải là khoảng thời gian dài nhất để một ý tưởng toán học được áp dụng vào thực tế. một nhân viên của Viện Công nghệ Massachusetts) bắt đầu quan tâm đến các ý tưởng của Church. Năm 1958, ông đã tạo ra ngôn ngữ lập trình chức năng đầu tiên, LISP, dựa trên những ý tưởng đó. Và 58 năm sau, ý tưởng về lập trình hàm rò rỉ vào Java 8. Chưa đầy 70 năm đã trôi qua... Thành thật mà nói, đây không phải là khoảng thời gian dài nhất để một ý tưởng toán học được áp dụng vào thực tế.

Trung tâm của vấn đề

Biểu thức lambda là một loại hàm. Bạn có thể coi nó là một phương thức Java thông thường nhưng có khả năng đặc biệt được truyền cho các phương thức khác làm đối số. Đúng rồi. Có thể truyền không chỉ các số, chuỗi và mèo cho các phương thức mà còn cả các phương thức khác! Khi nào chúng ta có thể cần điều này? Ví dụ, nó sẽ hữu ích nếu chúng ta muốn chuyển một số phương thức gọi lại. Nghĩa là, nếu chúng ta cần phương thức mà chúng ta gọi để có khả năng gọi một số phương thức khác mà chúng ta truyền cho nó. Nói cách khác, vì vậy chúng tôi có khả năng chuyển một cuộc gọi lại trong một số trường hợp nhất định và một cuộc gọi lại khác trong những trường hợp khác. Và do đó, phương thức nhận cuộc gọi lại của chúng tôi sẽ gọi chúng. Sắp xếp là một ví dụ đơn giản. Giả sử chúng ta đang viết một số thuật toán sắp xếp thông minh giống như sau:

public void mySuperSort() { 
    // We do something here 
    if(compare(obj1, obj2) > 0) 
    // And then we do something here 
}
Trong ifcâu lệnh, chúng ta gọi compare()phương thức, truyền vào hai đối tượng để so sánh và chúng ta muốn biết đối tượng nào trong số này là "lớn hơn". Chúng tôi cho rằng cái "lớn hơn" xuất hiện trước cái "nhỏ hơn". Tôi đặt "lớn hơn" trong dấu ngoặc kép, bởi vì chúng tôi đang viết một phương thức phổ quát sẽ biết cách sắp xếp không chỉ theo thứ tự tăng dần mà còn theo thứ tự giảm dần (trong trường hợp này, đối tượng "lớn hơn" sẽ thực sự là đối tượng "nhỏ hơn" , và ngược lại). Để đặt thuật toán cụ thể cho sắp xếp của chúng tôi, chúng tôi cần một số cơ chế để chuyển thuật toán đó sang mySuperSort()phương thức của chúng tôi. Bằng cách đó, chúng ta sẽ có thể "kiểm soát" phương thức của mình khi nó được gọi. Tất nhiên, chúng ta có thể viết hai phương thức riêng biệt — mySuperSortAscend()mySuperSortDescend()- để sắp xếp theo thứ tự tăng dần và giảm dần. Hoặc chúng ta có thể truyền một số đối số cho phương thức (ví dụ: biến boolean; nếu đúng thì sắp xếp theo thứ tự tăng dần và nếu sai thì sắp xếp theo thứ tự giảm dần). Nhưng nếu chúng ta muốn sắp xếp thứ gì đó phức tạp, chẳng hạn như danh sách các mảng chuỗi thì sao? Làm thế nào để mySuperSort()phương thức của chúng ta biết cách sắp xếp các mảng chuỗi này? Theo kích cỡ? Bằng độ dài tích lũy của tất cả các từ? Có lẽ theo thứ tự bảng chữ cái dựa trên chuỗi đầu tiên trong mảng? Và điều gì sẽ xảy ra nếu chúng ta cần sắp xếp danh sách các mảng theo kích thước mảng trong một số trường hợp và theo độ dài tích lũy của tất cả các từ trong mỗi mảng trong các trường hợp khác? Tôi hy vọng bạn đã nghe nói về bộ so sánh và trong trường hợp này, chúng ta chỉ cần chuyển đến phương pháp sắp xếp của mình một đối tượng so sánh mô tả thuật toán sắp xếp mong muốn. Bởi vì tiêu chuẩnsort()được triển khai dựa trên nguyên tắc giống như mySuperSort(), tôi sẽ sử dụng sort()trong các ví dụ của mình.

String[] array1 = {"Dota", "GTA5", "Halo"}; 
String[] array2 = {"I", "really", "love", "Java"}; 
String[] array3 = {"if", "then", "else"}; 

List<String[]> arrays = new ArrayList<>(); 
arrays.add(array1); 
arrays.add(array2); 
arrays.add(array3); 

Comparator<;String[]> sortByLength = new Comparator<String[]>() { 
    @Override 
    public int compare(String[] o1, String[] o2) { 
        return o1.length - o2.length; 
    } 
}; 

Comparator<String[]> sortByCumulativeWordLength = new Comparator<String[]>() { 

    @Override 
    public int compare(String[] o1, String[] o2) { 
        int length1 = 0; 
        int length2 = 0; 
        for (String s : o1) { 
            length1 += s.length(); 
        } 

        for (String s : o2) { 
            length2 += s.length(); 
        } 

        return length1 - length2; 
    } 
};

arrays.sort(sortByLength);
Kết quả:

  1. Dota GTA5 Halo
  2. if then else
  3. I really love Java
Ở đây các mảng được sắp xếp theo số từ trong mỗi mảng. Một mảng có ít từ hơn được coi là "ít hơn". Đó là lý do tại sao nó đến trước. Một mảng có nhiều từ hơn được coi là "lớn hơn" và được đặt ở cuối. Nếu chúng ta chuyển một bộ so sánh khác cho sort()phương thức, chẳng hạn như sortByCumulativeWordLength, thì chúng ta sẽ nhận được một kết quả khác:

  1. if then else
  2. Dota GTA5 Halo
  3. I really love Java
Bây giờ các mảng được sắp xếp theo tổng số chữ cái trong các từ của mảng. Trong mảng đầu tiên, có 10 chữ cái, trong mảng thứ hai — 12 và trong mảng thứ ba — 15. Nếu chúng ta chỉ có một bộ so sánh duy nhất, thì chúng ta không phải khai báo một biến riêng cho nó. Thay vào đó, chúng ta có thể chỉ cần tạo một lớp ẩn danh ngay tại thời điểm gọi phương sort()thức. Một cái gì đó như thế này:

String[] array1 = {"Dota", "GTA5", "Halo"}; 
String[] array2 = {"I", "really", "love", "Java"}; 
String[] array3 = {"if", "then", "else"}; 

List<String[]> arrays = new ArrayList<>(); 

arrays.add(array1); 
arrays.add(array2); 
arrays.add(array3); 

arrays.sort(new Comparator<String[]>() { 
    @Override 
    public int compare(String[] o1, String[] o2) { 
        return o1.length - o2.length; 
    } 
}); 
Chúng tôi sẽ nhận được kết quả tương tự như trong trường hợp đầu tiên. Nhiệm vụ 1. Viết lại ví dụ này để nó sắp xếp các mảng không theo thứ tự tăng dần của số từ trong mỗi mảng mà theo thứ tự giảm dần. Chúng tôi đã biết tất cả điều này. Chúng tôi biết cách truyền đối tượng cho phương thức. Tùy thuộc vào những gì chúng ta cần vào lúc này, chúng ta có thể chuyển các đối tượng khác nhau cho một phương thức, phương thức này sau đó sẽ gọi phương thức mà chúng ta đã triển khai. Điều này đặt ra câu hỏi: tại sao trên thế giới chúng ta cần một biểu thức lambda ở đây?  Bởi vì một biểu thức lambda là một đối tượng có chính xác một phương thức. Giống như một "đối tượng phương pháp". Một phương thức được đóng gói trong một đối tượng. Nó chỉ có một cú pháp hơi lạ (nhưng sẽ nói thêm về điều đó sau). Hãy xem xét lại đoạn mã này:

arrays.sort(new Comparator<String[]>() { 
    @Override 
    public int compare(String[] o1, String[] o2) { 
        return o1.length - o2.length; 
    } 
});
Ở đây, chúng tôi lấy danh sách mảng của mình và gọi sort()phương thức của nó, mà chúng tôi chuyển một đối tượng so sánh bằng một compare()phương thức duy nhất (tên của nó không quan trọng đối với chúng tôi - xét cho cùng, đó là phương thức duy nhất của đối tượng này, vì vậy chúng tôi không thể sai). Phương thức này có hai tham số mà chúng ta sẽ làm việc với. Nếu bạn đang làm việc trong IntelliJ IDEA, có thể bạn đã thấy nó đề xuất cô đọng mã một cách đáng kể như sau:

arrays.sort((o1, o2) -> o1.length - o2.length);
Điều này làm giảm sáu dòng thành một dòng ngắn. 6 dòng được viết lại thành 1 dòng ngắn. Một cái gì đó đã biến mất, nhưng tôi đảm bảo nó không có gì quan trọng. Mã này sẽ hoạt động chính xác giống như với một lớp ẩn danh. Nhiệm vụ 2. Đoán cách viết lại giải pháp cho Nhiệm vụ 1 bằng cách sử dụng biểu thức lambda (ít nhất, hãy yêu cầu IntelliJ IDEA chuyển đổi lớp ẩn danh của bạn thành biểu thức lambda).

Hãy nói về giao diện

Về nguyên tắc, một giao diện đơn giản là một danh sách các phương thức trừu tượng. Khi chúng ta tạo một lớp cài đặt một số giao diện, lớp của chúng ta phải triển khai các phương thức có trong giao diện (hoặc chúng ta phải tạo lớp trừu tượng). Có những giao diện có nhiều phương thức khác nhau (ví dụ:  List) và có những giao diện chỉ có một phương thức (ví dụ: Comparatorhoặc Runnable). Có những giao diện không có một phương thức duy nhất (cái gọi là giao diện đánh dấu chẳng hạn như Serializable). Giao diện chỉ có một phương thức còn được gọi là giao diện chức năng . Trong Java 8, chúng thậm chí còn được đánh dấu bằng một chú thích đặc biệt:@FunctionalInterface. Chính các giao diện một phương thức này phù hợp làm loại mục tiêu cho các biểu thức lambda. Như tôi đã nói ở trên, biểu thức lambda là một phương thức được bọc trong một đối tượng. Và khi chúng ta truyền một đối tượng như vậy, về cơ bản chúng ta đang truyền phương thức duy nhất này. Hóa ra là chúng ta không quan tâm phương thức đó được gọi là gì. Điều duy nhất quan trọng đối với chúng tôi là các tham số của phương thức và tất nhiên là phần thân của phương thức. Về bản chất, biểu thức lambda là triển khai giao diện chức năng. Bất cứ nơi nào chúng ta thấy một giao diện với một phương thức duy nhất, một lớp ẩn danh có thể được viết lại dưới dạng lambda. Nếu giao diện có nhiều hoặc ít hơn một phương thức, thì biểu thức lambda sẽ không hoạt động và thay vào đó, chúng ta sẽ sử dụng một lớp ẩn danh hoặc thậm chí là một thể hiện của một lớp thông thường. Bây giờ là lúc tìm hiểu sâu về lambdas một chút. :)

cú pháp

Cú pháp chung là như thế này:

(parameters) -> {method body}
Nghĩa là, các dấu ngoặc đơn bao quanh các tham số của phương thức, một "mũi tên" (được tạo bởi dấu gạch nối và dấu lớn hơn), và sau đó là phần thân phương thức trong dấu ngoặc nhọn, như mọi khi. Các tham số tương ứng với các tham số được chỉ định trong phương thức giao diện. Nếu các loại biến có thể được xác định rõ ràng bởi trình biên dịch (trong trường hợp của chúng tôi, nó biết rằng chúng tôi đang làm việc với các mảng chuỗi, vì Listđối tượng của chúng tôi được nhập bằng String[]), thì bạn không cần phải chỉ ra các loại của chúng.
Nếu chúng không rõ ràng, thì hãy chỉ ra loại. IDEA sẽ tô màu xám nếu không cần thiết.
Bạn có thể đọc thêm trong hướng dẫn Oracle này và các nơi khác. Điều này được gọi là " gõ mục tiêu ". Bạn có thể đặt tên cho các biến theo bất cứ điều gì bạn muốn — bạn không cần phải sử dụng các tên giống nhau được chỉ định trong giao diện. Nếu không có tham số, thì chỉ cần chỉ ra dấu ngoặc đơn trống. Nếu chỉ có một tham số, chỉ cần chỉ ra tên biến mà không có bất kỳ dấu ngoặc đơn nào. Bây giờ chúng ta đã hiểu các tham số, đã đến lúc thảo luận về phần thân của biểu thức lambda. Bên trong dấu ngoặc nhọn, bạn viết mã giống như đối với một phương thức thông thường. Nếu mã của bạn bao gồm một dòng đơn, thì bạn có thể bỏ qua hoàn toàn dấu ngoặc nhọn (tương tự như câu lệnh if và vòng lặp for). Nếu lambda một dòng của bạn trả về nội dung nào đó, thì bạn không cần phải bao gồmreturntuyên bố. Nhưng nếu bạn sử dụng dấu ngoặc nhọn, thì bạn phải bao gồm một returncâu lệnh một cách rõ ràng, giống như bạn làm trong một phương pháp thông thường.

ví dụ

Ví dụ 1.

() -> {}
Ví dụ đơn giản nhất. Và điều vô nghĩa nhất :), vì nó chẳng làm được gì cả. Ví dụ 2.

() -> ""
Một ví dụ thú vị khác. Nó không lấy gì cả và trả về một chuỗi rỗng ( returnđược bỏ qua, vì nó không cần thiết). Đây là điều tương tự, nhưng với return:

() -> { 
    return ""; 
}
Ví dụ 3. "Xin chào, Thế giới!" sử dụng lambda

() -> System.out.println("Hello, World!")
Nó không nhận gì và không trả về gì (chúng ta không thể đặt returntrước lệnh gọi System.out.println(), vì println()kiểu trả về của phương thức là void). Nó chỉ đơn giản là hiển thị lời chào. Điều này là lý tưởng cho việc thực hiện giao Runnablediện. Ví dụ sau đầy đủ hơn:

public class Main { 
    public static void main(String[] args) { 
        new Thread(() -> System.out.println("Hello, World!")).start(); 
    } 
}
Hoặc như thế này:

public class Main { 
    public static void main(String[] args) { 
        Thread t = new Thread(() -> System.out.println("Hello, World!")); 
        t.start();
    } 
}
Hoặc thậm chí chúng ta có thể lưu biểu thức lambda dưới dạng một Runnableđối tượng và sau đó chuyển nó tới Threadhàm tạo:

public class Main { 
    public static void main(String[] args) { 
        Runnable runnable = () -> System.out.println("Hello, World!"); 
        Thread t = new Thread(runnable); 
        t.start(); 
    } 
}
Chúng ta hãy xem xét kỹ hơn thời điểm khi một biểu thức lambda được lưu vào một biến. Giao Runnablediện cho chúng ta biết rằng các đối tượng của nó phải có một public void run()phương thức. Theo giao diện, runphương thức này không có tham số. Và nó không trả về gì cả, tức là kiểu trả về của nó là void. Theo đó, mã này sẽ tạo một đối tượng với một phương thức không nhận hoặc trả lại bất kỳ thứ gì. Điều này hoàn toàn phù hợp với phương thức Runnablecủa giao diện run(). Đó là lý do tại sao chúng tôi có thể đặt biểu thức lambda này vào một Runnablebiến.  Ví dụ 4.

() -> 42
Một lần nữa, nó không nhận gì cả, nhưng nó trả về số 42. Biểu thức lambda như vậy có thể được đặt trong một Callablebiến, bởi vì giao diện này chỉ có một phương thức giống như thế này:

V call(),
V kiểu trả về ở  đâu  (trong trường hợp của chúng ta là int). Theo đó, chúng ta có thể lưu một biểu thức lambda như sau:

Callable<Integer> c = () -> 42;
Ví dụ 5. Một biểu thức lambda gồm nhiều dòng

() -> { 
    String[] helloWorld = {"Hello", "World!"}; 
    System.out.println(helloWorld[0]); 
    System.out.println(helloWorld[1]); 
}
Một lần nữa, đây là biểu thức lambda không có tham số và voidkiểu trả về (vì không có returncâu lệnh).  Ví dụ 6

x -> x
Ở đây chúng tôi lấy một xbiến và trả về nó. Xin lưu ý rằng nếu chỉ có một tham số thì bạn có thể bỏ qua dấu ngoặc đơn xung quanh tham số đó. Đây là điều tương tự, nhưng với dấu ngoặc đơn:

(x) -> x
Và đây là một ví dụ với câu lệnh return rõ ràng:

x -> { 
    return x;
}
Hoặc như thế này với dấu ngoặc đơn và câu lệnh trả về:

(x) -> { 
    return x;
}
Hoặc với một dấu hiệu rõ ràng của loại (và do đó với dấu ngoặc đơn):

(int x) -> x
Ví dụ 7

x -> ++x
Chúng tôi lấy xvà trả lại, nhưng chỉ sau khi thêm 1. Bạn có thể viết lại lambda đó như thế này:

x -> x + 1
Trong cả hai trường hợp, chúng tôi bỏ qua dấu ngoặc đơn xung quanh thân tham số và phương thức, cùng với returncâu lệnh, vì chúng là tùy chọn. Các phiên bản có dấu ngoặc đơn và câu lệnh return được đưa ra trong Ví dụ 6. Ví dụ 8

(x, y) -> x % y
Chúng tôi lấy xytrả lại phần còn lại của phép chia xcho y. Các dấu ngoặc đơn xung quanh các tham số được yêu cầu ở đây. Chúng chỉ là tùy chọn khi chỉ có một tham số. Đây là với một dấu hiệu rõ ràng của các loại:

(double x, int y) -> x % y
Ví dụ 9

(Cat cat, String name, int age) -> {
    cat.setName(name); 
    cat.setAge(age); 
}
Chúng tôi lấy một Catđối tượng, một Stringcái tên và một int age. Trong chính phương thức, chúng tôi sử dụng tên và tuổi đã truyền để đặt các biến trên con mèo. Bởi vì catđối tượng của chúng ta là một kiểu tham chiếu, nên nó sẽ được thay đổi bên ngoài biểu thức lambda (nó sẽ lấy tên và tuổi đã truyền). Đây là phiên bản phức tạp hơn một chút sử dụng lambda tương tự:

public class Main { 

    public static void main(String[] args) { 
        // Create a cat and display it to confirm that it is "empty" 
        Cat myCat = new Cat(); 
        System.out.println(myCat);
 
        // Create a lambda 
        Settable<Cat> s = (obj, name, age) -> { 
            obj.setName(name); 
            obj.setAge(age); 

        }; 

        // Call a method to which we pass the cat and lambda 
        changeEntity(myCat, s); 

        // Display the cat on the screen and see that its state has changed (it has a name and age) 
        System.out.println(myCat); 

    } 

    private static <T extends HasNameAndAge>  void changeEntity(T entity, Settable<T> s) { 
        s.set(entity, "Smokey", 3); 
    }
}

interface HasNameAndAge { 
    void setName(String name); 
    void setAge(int age); 
}

interface Settable<C extends HasNameAndAge> { 
    void set(C entity, String name, int age); 
}

class Cat implements HasNameAndAge { 
    private String name; 
    private int age; 

    @Override 
    public void setName(String name) { 
        this.name = name;
    }

    @Override
    public void setAge(int age) {
        this.age = age; 
    } 

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' + 
                ", age=" + age + 
                '}';
    }
}
Kết quả:

Cat{name='null', age=0}
Cat{name='Smokey', age=3}
Như bạn có thể thấy, Catđối tượng có một trạng thái và sau đó trạng thái đó thay đổi sau khi chúng ta sử dụng biểu thức lambda. Biểu thức lambda kết hợp hoàn hảo với thuốc generic. Và nếu chúng ta cần tạo một Doglớp cũng thực hiện HasNameAndAgethì chúng ta có thể thực hiện các thao tác tương tự trong Dogphương main() thức mà không thay đổi biểu thức lambda. Nhiệm vụ 3. Viết giao diện chức năng với phương thức nhận vào một số và trả về giá trị boolean. Viết cách triển khai một giao diện như vậy dưới dạng biểu thức lambda trả về true nếu số được truyền chia hết cho 13. Nhiệm vụ 4.Viết một giao diện chức năng với một phương thức nhận hai chuỗi và cũng trả về một chuỗi. Viết triển khai giao diện như vậy dưới dạng biểu thức lambda trả về chuỗi dài hơn. Nhiệm vụ 5. Viết một giao diện chức năng với một phương thức nhận ba số dấu phẩy động: a, b và c và cũng trả về một số dấu phẩy động. Viết triển khai giao diện như vậy dưới dạng biểu thức lambda trả về giá trị phân biệt. Trong trường hợp bạn quên, đó là D = b^2 — 4ac. Nhiệm vụ 6. Sử dụng giao diện chức năng từ Nhiệm vụ 5, viết biểu thức lambda trả về kết quả của a * b^c. Giải thích về các biểu thức lambda trong Java. Với các ví dụ và nhiệm vụ. Phần 2
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION