Chúng ta biết đến Java là một ngôn ngữ lập trình hướng đối tượng rất phổ biến. Từ version 8, Java cung cấp thêm một cách thức viết code mang style funtional programing với lambda expression.
Lambda expression không phải là khái niệm thuộc về chỉ mình Java, có thể trước đây bạn đã nghe thấy ở những ngôn ngữ khác rồi. Nó còn được biết đến với cái tên ngắn gọn hơn là "Lambda".
functional interface
.
Functional interface là interface có một và chỉ một abstract method (Không bao gồm những abstract method được kế thừa từ Object class).
Functional interface có thể có nhiều default và static method.
Dựa trên định nghĩa hãy xác định những interface dưới đây có phải functional interface hay không?
public interface AbstractAction { } public interface CandidateAction { void interview(); } public interface FresherCandidateAction extends CandidateAction { void interview(); } public interface JuniorCandidateAction { void interview(); void interviewRound02(); } public interface SeniorCandidateAction { void interview(); default void interviewWithHr() { }; }
AbstractAction
là functional interface.CandidateAction
, FresherCandidateAction
là functional interface.CandidateAction
, FresherCandidateAction
, JuniorCandidateAction
là functional interface.CandidateAction
, FresherCandidateAction
, SeniorCandidateAction
là functional interface.D
AbstractAction
không phải là functional interface vì chỉ có không có abstract method nào.CandidateAction
là functional interface vì chỉ có 1 abstract method.FresherCandidateAction
dù extends CandidateAction
những vẫn chỉ có 1 abstract method do method signarure giống nhau nên vẫn là functional interfaceJuniorCandidateAction
có 2 abstract method nên KHÔNG phải là functional interface.SeniorCandidateAction
có 2 methods tuy nhiên 1 default method và 1 abstract method nên vẫn là functional interface.Lambda expression là một anonymous method (unnamed method) có thể thực hiện công việc như một method thông thường.
Lambda expression implement một abstract method được định nghĩa trong interface method.
Cú pháp của lambda expression mà dễ bị nhầm lẫn do nhiều phần là optional. Lambda expression bao gồm 3 phần:
parameter_list
: Danh sách parameter đầu vào.
arrow operator
: ->function_body
:
parameter -> expression
(data_type parameter1, data_type parameter2) -> expression
(paramater1, parameter2) -> { function_body }
Sau đây là ví dụ 5 valid lambda expressions
// 01. Expression không có tham số và luôn return true () -> true // 02. Expression nhận vào 1 String parameter và call method startsWith s -> s.startsWith("winzone") // 03. Tương tự 02 chỉ khác là chỉ định rõ data type của parameter (String s) -> s.startsWith("winzone") // 04: Expression nhận vào 2 tham số và call method equals (s1, s2) -> s1.equals(s2) // 05: Tương tự 04 chỉ khác là chỉ định rõ data type của parameter (String s1, String s2) -> s1.equals(s2)
Sau đây là ví dụ 3 invalid lambda expressions
// 01. Thiếu () khi expression có 2 parameters s1, s2 -> s1.equals(s2) // 02. Thiếu keyword "return" do đang được wrap trong {} s1 -> { s1.startsWith("winzone"); } // 03. Thiếu dấu ";" s1 -> { return s1.startsWith("winzone") }
Để hiểu và vận dụng được lambda, chúng ta cùng nhau triển khai source code của yêu cầu như sau:
Project bao gồm 3 classes/interfaces với những chức năng nhiệm vụ như sau:
pass()
, sẽ return true nếu điểm entryTest thoả mãn điều kiện, ngược lại sẽ return false.
displayResult()
với mục đích hiển thị kết quả của từng ứng viên (Qualified/Unqualified). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package winzonevn.training.entity; /** * @author <a href="mailto:phuongdp.tech@gmail.com">PhuongDP</a> */ public class Candidate { private String email; private float entryTest; public Candidate() { } public Candidate(String email, float entryTest) { this.email = email; this.entryTest = entryTest; } // Getter/Setter } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package winzonevn.training.function; /** * @author <a href="mailto:phuongdp.tech@gmail.com">PhuongDP</a> */ @FunctionalInterface public interface PassingCriteria { /** * Check Candidate is Qualified or Unqualified * * @param entryTest: Candidate's entryTest * @return * If Candidate's entryTest is passing criteria return true * otherwise return false */ boolean pass(float entryTest); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package winzonevn.training.service; import winzonevn.training.entity.Candidate; import winzonevn.training.function.PassingCriteria; import java.util.List; /** * @author <a href="mailto:phuongdp.tech@gmail.com">PhuongDP</a> */ public class CandidateService { public void displayResult(List<Candidate> candidates, PassingCriteria criteria) { for (Candidate candidate : candidates) { if (criteria.pass(candidate.getEntryTest())) { System.out.printf("Candidate: %s is QUALIFIED with entry test = %.2f!\n", candidate.getEmail(), candidate.getEntryTest()); } else { System.out.printf("Candidate: %s is UNQUALIFIED with entry test = %.2f!\n", candidate.getEmail(), candidate.getEntryTest()); } } } } |
Chúng ta có yêu cầu của đề như sau:
Với yêu cầu của đề bài chúng ta sẽ có 2 công việc cần thực hiện:
- Implement phần kiểm tra điểm entry test bằng cách override method abstract PassingCriteria.pass()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package winzonevn.training.function.impl; import winzonevn.training.function.PassingCriteria; /** * @author <a href="mailto:phuongdp.tech@gmail.com">PhuongDP</a> */ public class FresherPassingCriteria implements PassingCriteria { @Override public boolean pass(float entryTest) { return entryTest >= 6; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package winzonevn.training.function.impl; import winzonevn.training.function.PassingCriteria; /** * @author <a href="mailto:phuongdp.tech@gmail.com">PhuongDP</a> */ public class SeniorPassingCriteria implements PassingCriteria { @Override public boolean pass(float entryTest) { return entryTest >= 8.5; } } |
- Tạo class CandidateManagement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package winzonevn.training; import winzonevn.training.entity.Candidate; import winzonevn.training.function.impl.SeniorPassingCriteria; import winzonevn.training.function.impl.FresherPassingCriteria; import winzonevn.training.service.CandidateService; import java.util.Arrays; import java.util.List; /** * @author <a href="mailto:phuongdp.tech@gmail.com">PhuongDP</a> */ public class CandidateManagement { public static void main(String[] args) { CandidateService candidateService = new CandidateService(); Candidate candidate01 = new Candidate("candidate01@gmail.com", 7); Candidate candidate02 = new Candidate("candidate02@gmail.com", 9); Candidate candidate03 = new Candidate("candidate03@gmail.com", 6); List<Candidate> candidateList = Arrays.asList(candidate01, candidate02, candidate03); System.out.println("Candidate apply FRESHER position: "); candidateService.displayResult(candidateList, new FresherPassingCriteria()); System.out.println("\n-----------------------------------"); System.out.println("\nCandidate apply SENIOR position: "); candidateService.displayResult(candidateList, new SeniorPassingCriteria()); } } |
Execute main()
method và ta có kết quả như sau:
Candidate apply FRESHER position: Candidate: candidate01@gmail.com is QUALIFIED with entry test = 7.00! Candidate: candidate02@gmail.com is QUALIFIED with entry test = 9.00! Candidate: candidate03@gmail.com is QUALIFIED with entry test = 6.00! ----------------------------------- Candidate apply SENIOR position: Candidate: candidate01@gmail.com is UNQUALIFIED with entry test = 7.00! Candidate: candidate02@gmail.com is QUALIFIED with entry test = 9.00! Candidate: candidate03@gmail.com is UNQUALIFIED with entry test = 6.00!
Với cách triển khai bên trên chúng ta đã đáp ứng được yêu cầu đề bài khi đã verify được chất lượng ứng viên theo từng vị trí apply.
Tuy nhiên cách triển khai này có một yếu điểm đó là: với mỗi vị trí (Fresher, Senior...) chúng ta sẽ cần phải tạo ra 1 class implement interface PassingCriteria.Đây là một cách triển khai khiến chúng ta phải tạo nhiều class không cần thiết.
Anonymous classes là 1 expression cho phép bạn khai báo và khởi tạo instance của class cùng 1 thời điểm và đặc biệt không cần chỉ định tên Class.
Yếu điểm của cách triển khai source 1.1 đó là dài dòng khi phải tạo nhiều class tương ứng với mỗi loại position (Fresher, Senior). Ta có thể tận dụng tính năng của anonymous classes để triển khai source như sau:
- Tạo class CandidateManagementWithAnonymousClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package winzonevn.training; import winzonevn.training.entity.Candidate; import winzonevn.training.function.PassingCriteria; import winzonevn.training.service.CandidateService; import java.util.Arrays; import java.util.List; public class CandidateManagementWithAnonymousClass { public static void main(String[] args) { CandidateService candidateService = new CandidateService(); Candidate candidate01 = new Candidate("candidate01@gmail.com", 7); Candidate candidate02 = new Candidate("candidate02@gmail.com", 9); Candidate candidate03 = new Candidate("candidate03@gmail.com", 6); List<Candidate> candidateList = Arrays.asList(candidate01, candidate02, candidate03); System.out.println("Candidate apply FRESHER position: "); candidateService.displayResult(candidateList, new PassingCriteria() { @Override public boolean pass(float entryTest) { return entryTest >= 6; } }); System.out.println("\n-----------------------------------"); System.out.println("\nCandidate apply SENIOR position: "); candidateService.displayResult(candidateList, new PassingCriteria() { @Override public boolean pass(float entryTest) { return entryTest >= 7; } }); } } |
Execute main()
method và ta có kết quả KHÔNG THAY ĐỔI.
Với cách triển khai bên trên chúng ta cũng đáp ứng được yêu cầu đề bài khi đã verify được chất lượng ứng viên theo từng vị trí apply.
Với cách dùng anonymous classes ta đã giảm bớt được 2 class cần phải tạo làFresherPassingCriteria, SeniorPassingCriteria
.
Tuy nhiên trong class CandidateManagementWithAnonymousClass
bạn có thể thấy chưa thực sự hoàn hảo khi 2 blocks dòng 21-26 và 30-35 source code đang duplicate và chỉ khác điều kiện so sánh.
Để giải quyết các vấn đề tạo thừa class không cần thiết, duplicate source của 2 cách bên trên, chúng ta sẽ cùng nhau triển khai theo các dùng Lambda Expression.
- Tạo class CandidateMngtByLambdaExpression
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package winzonevn.training; import winzonevn.training.entity.Candidate; import winzonevn.training.service.CandidateService; import java.util.Arrays; import java.util.List; public class CandidateMngtByLambdaExpression { public static void main(String[] args) { Candidate candidate01 = new Candidate("candidate01@gmail.com", 7); Candidate candidate02 = new Candidate("candidate02@gmail.com", 9); Candidate candidate03 = new Candidate("candidate03@gmail.com", 6); List<Candidate> candidateList = Arrays.asList(candidate01, candidate02, candidate03); CandidateService candidateService = new CandidateService(); // Check for Fresher position candidateService.displayResult(candidateList, entryTest -> entryTest >= 6); // Other ways // candidateService.displayResult(candidateList, (float entryTest) -> entryTest >= 6); // candidateService.displayResult(candidateList, (float entryTest) -> {return entryTest >= 6; }); // Check for Senior position candidateService.displayResult(candidateList, entryTest -> entryTest >= 8.5); } } |
Execute main()
method và ta có kết quả cũng KHÔNG THAY ĐỔI.
Với những khái niệm về functional interfaces, lambda expressions ta có thể loại bỏ việc phải tạo thừa class cũng như duplicate source.
Làm thế nào mà lambda line 20 entryTest -> entryTest >= 6
có thể execute tương tự như FresherPassingCriteria.pass()
của cách 01. Tham khảo cách giải thích trong figure 02.
Hi vọng rằng bài viết này đã cung cấp đến cho các bạn các kiến thức cơ bản về cú pháp, cách dùng và cách thức hoạt động của lambda expression. Hãy thực hành thật nhiều đề thành thạo với lambda, vì còn khá nhiều các topic mới của Java 8 chúng ta sẽ cần sử dụng đến nó.