[Java 8] Lambda expressions và Method References
Java 8 đã được release vào năm 2014, tuy nhiên hiện nay còn nhiều lập trình viên vẫn chưa hiểu và chưa sử dụng một số những tính năng mới, rất tiện lợi của phiên bản này. Trong bài viết lần này chúng ta sẽ cùng nhau tìm hiểu về Lambda expressions và Method references, hai tính năng khá đặc biệt của ...
Java 8 đã được release vào năm 2014, tuy nhiên hiện nay còn nhiều lập trình viên vẫn chưa hiểu và chưa sử dụng một số những tính năng mới, rất tiện lợi của phiên bản này. Trong bài viết lần này chúng ta sẽ cùng nhau tìm hiểu về Lambda expressions và Method references, hai tính năng khá đặc biệt của Java 8.
Lambda expressions
Trước hết chúng ta sẽ cùng tìm hiểu một khái niệm là functional interface. Functional interface là interface chỉ có duy nhất một phương thức trừu tượng (Single Abstract Method - SMA). Như chúng ta đã biết, một lớp khi implement một interface thì class đó phải override toàn bộ method của interface trong body của mình. Chúng ta sẽ lấy một ví dụ về Runnable interface, interface này đã có từ phiên bản Java 1.0. Nó chỉ có duy nhất một phương thức trừu tượng là run, phương thức này không cần tham số và kiểu trả về là void. Một Thread sẽ có constructor với tham số chính là Runnable, khi đó một anonymous inner class sẽ được tạo thành
public class Main { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("inside runnable using an anonymous inner class"); } }).start(); } }
Khi chúng ta sử dụng Lambda, đoạn code trên sẽ ngắn gọn hơn rất nhiều
public class Main { public static void main(String[] args) { new Thread(() -> System.out.println("inside runnable using an anonymous inner class")).start(); } }
Cú pháp Lambda sử dụng một arrow (mũi tên), để chỉ định tham số được sử dụng trong body. Trong trường hợp này, phương thức trừu tượng run ko có tham số nên chỉ cần () ->. Thân hàm trong trường hợp này chỉ có một câu lệnh nên chúng ta ko cần để khối lệnh trọn {}. Đây chính là một expression lambda. Giá trị của expression sẽ được trả về tự động. Ở đây giá trị trả về của phương thức println là void, do đó giá trị trả về từ expression cũng là void và phù hợp với giá trị trả về của phương thức run.
Một lambda expression phải phù hợp với kiểu tham số và kiểu trả về (hay còn gọi là chữ ký) của SMA. Một cách khác chúng ta cũng có thể assign lambda cho một biến, ví dụ
Runnable r = () -> System.out.println("lambda expression implementing the run method"); new Thread(r).start();
Chúng ta cũng có thể hiểu lambda chính là body của anonymous inner class kế thừa interface. Điều này cũng giải thích cho việc vì sao lambda phải phù hợp với phương thức trừu tượng, hay nói cách khác là phải giống nhau về chữ ký. Tuy nhiên, chú ý là với lambda thì tên của phương thức trừu tượng là không quan trọng. Chúng ta không cần khai báo ở bất cứ đâu trong cú pháp của lambda.
Ví dụ trên là một ví dụ đơn giản về việc sử dụng lambda, vì phương thức run return void và không có tham số. Chúng ta sẽ xem xét một ví dụ khác là java.io.FilenamFilter, một functional interface cũng có từ phiên bản java 1.0. Một instance của FilenameFilter được sử dụng là tham số của File.list method.
Chúng ta có thể tìm thấy định nghĩa của FilenameFilter trong Javadoc như sau
boolean accept(File dir, String name)
Tham số đầu tiên chính là đường dẫn mà có thể tìm thấy file, tham số thứ hai chính là tên của file. Ví dụ sau đây chúng ta sẽ tìm những file mà có extension là .java trong đường dẫn chỉ định.
File directory = new File("./src/main/java"); String[] names = directory.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".java"); } }); System.out.println(Arrays.asList(names));
Đối với lambda, đoạn code trên sẽ trở thành
File directory = new File("./src/main/java"); String[] names = directory.list((dir, name) -> name.endsWith(".java")); System.out.println(Arrays.asList(names));
Qua ví dụ trên chúng ta thấy trong cú pháp lambda, chúng ta không cần thể hiện kiểu của tham số. Khi compile, trình compile sẽ biết rằng phương thức list cần 1 tham số kiểu FilenameFilter, vì vậy nó cũng biết được kiểu tham số của phương thức SMA (accept) là File và String. Tuy nhiên, chúng ta cũng hoàn toàn có thể thể hiện kiểu dữ liệu của tham số (tuy nhiên điều này là không cần thiết