Khi làm việc với các loại dữ liệu tổng quát, và các hàm phải đưa ra các kết quả với các kiểu khác nhau, thì việc áp dụng Monadic Operations giúp bạn dễ dàng tạo ra nhiều hàm như vậy.. Th
Trang 1Trường Đại học Bách Khoa Hà Nội
Viện CNTT-TT
Lập trình Java.
Đề tài: Tìm hiểu về Monadic Operations.
Thành viên: MSSV Giáo viên hướng dẫn
Đậu Văn Thắng 20112678 Nguyễn Hồng Quang
Hà Nội, 8/9/2014
Trang 2Contents
Trang 3I Lý thuyết.
3 PROGRAMING WITH LAMDAS
3.10 Monadic Operations
Khi làm việc với các loại dữ liệu tổng quát, và các hàm phải đưa ra các kết quả (với các kiểu khác nhau), thì việc áp dụng Monadic Operations giúp bạn dễ dàng tạo ra nhiều hàm như vậy Trong phần này chúng ta sẽ cùng nhau tìm hiểu mẫu thiết kế cho Monadic Operations
Giả sử chúng ta có một dữ liệu tổng quát là G<T> với một tham số Nó bao gồm một số
dữ liệu cụ thể hơn như:
1. List<T>: Bao gồm một hoặc nhiều giá trị loại T
2. Optional<T>: Không có giá trị hoặc chỉ có một giá trị loại T
3. Future<T>: Phải có ít nhật một giá trị loại T trong danh sách
Chúng ta cũng giả sử có một function T U hoặc coi như một đối tượng Function< T,U >
Thông thường thì chúng ta sẽ phải làm việc với các dữ liệu G<T> (dữ liệu tổng quát) thay
vì làm việc với các dữ liệu cụ thể hơn như List<T>, Optional<T> hay Future<T> Vậy làm sao có thể thực hiện một cách chính xác khi làm việc với G<T> Ví dụ như khi áp dụng hàm f với một List gồm phần tử: e1, e2, …, en nghĩa là phải tạo ra một danh sách các phần tử f(e1), f(e2),…, f(en)
Áp dụng hàm f cho Optional<T> chứa phần tử v nghĩa là phải tạo Optional<U> chứa f(v) Nhưng nếu khi áp dụng f với Optional<T> không có phần tử nào thì dĩ nhiên Optional<U> cũng sẽ không có một phần tử nào
Áp dụng hàm f cho Future<T> thì sẽ được thực hiện chỉ khi nó có giá trị Kết quả trả về
là Future<U>
Thông thường khi tiến hành xử lý thì chúng ta sẽ sử dụng một phương thức được gọi là
“map” Hiểu đơn giản “map” sẽ giúp chúng ta áp dụng hàm f tạo ra G<U> từ G<T> trong
đó khi áp dụng f thì T U, việc này sẽ được xử lý ở mỗi phần tử trong hàm dó đó nó sẽ
dễ dàng tiến hành với các trường hợp cụ thể như List<T>, Optional<T> hay Future<T> Chúng ta có thể thấy “map” là một ý tưởng khá là đơn giản Chúng ta có thể làm phức tạp hơn khi nhìn vào function T G<U> thay vì T U Một ví dụ khi chúng ta lấy nội dung từ trên web thông qua URL Chúng ta sẽ phải một một ít thời gian để lấy được trang web về, sau đó sẽ thực hiện function URL Future<String> Chúng ta sẽ chờ đợi URL đến, sau đó cho nó vào Function và chờ đợi chuỗi string trả về Quá trình hoạt động như vậy gọi flatMap
Trang 4Dạng URL Future<String> chính là một trường hợp cụ thể của “flatMap” Thay vì chuyển từ đối tượng G<T> G<U> nó tiến hành chuyển từ T G<U>
Ở đây sử dụng flatMap cho Optional<T> cũng được Chúng ta sẽ có function T Optional<U>, flatMap sẽ giải nén các giá trị trong Optional và áp dụng vào function (trừ khi cả giá trị ban đầu và giá trị kết quả đều không tồn tại)
Trên đây là những kiến thức về Monadic Operations Đây là một kiến thức khá mới nên mới đầu đọc còn khó hiểu sau đây chúng ta sẽ cùng tìm hiểu một ví dụ để có thể hiểu rõ hơn về chủ đề này
public class Optional<T> {
private static final Optional<?> EMPTY = new Optional<>(null);
private final T value;
private Optional(T value) {
this.value = value;
}
public<U> Optional<U> map(Function<? super T, ? extends U> f) {
return value == null ? EMPTY : new Optional(f.apply(value));
}
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> f) {
return value == null ? EMPTY : f.apply(value);
}
}
Nhìn vào class Optional<T> ta có thể thấy
Phương thức map: Đơn giản là sử dụng hàm f: Optional<T> Optional<U> trong đó
U = f.apply(value)
Phương thức flatMap: Nó không tạo ra một giá trị mới Optional<U> thông qua Optional<T> mà nó sẽ phân tích trong Optional và trả về một đối tượng khác thông qua hàm f.apply(value)
Sau đây ta sẽ có một ví dụ chi tiết sau: Để tìm tên bảo hiểm của xe của một chủ xe, thông thường ta sẽ thực hiện khai báo các lớp như sau:
public class Person {
private Car car;
public Car getCar() { return car; }
}
public class Car {
private Insurance insurance;
public Insurance getInsurance() { return insurance; }
Trang 5public class Insurance {
private String name;
public String getName() { return name; }
}
Và đề lấy được tên bảo hiểm ta phải sử dụng biểu thức
String getCarInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance;
if (insurance != null) {
return insurance.getName()
}
}
}
return "Unknown";
}
Việc tìm như thế này khá là mất công và phải duyệt tương đối sâu vào các lớp
Nếu không sử dụng cách trên ta có thể sử dụng cách sau:
String getCarInsuranceName(Person person) {
if (person == null) {
return "Unknown";
}
Car car = person.getCar();
if (car == null) {
return "Unknown";
}
Insurance insurance = car.getInsurance();
if (insurance == null) {
return "Unknown";
}
return insurance.getName()
}
Thì khi áp dụng cách này thì ta sẽ có rất nhiều lựa chọn (thông qua nhiều câu lệnh if) nhìn khá tốn code khi lập trình
Trang 6Ta sẽ khởi tạo 3 lớp như sau:
public class Person {
private Optional<Car> car;
public Optional<Car> getCar() { return car; }
}
public class Car {
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance() { return insurance; }
}
public class Insurance {
private String name;
public String getName() { return name; }
}
Việc áp dụng phương pháp Optional thì khi muốn lấy tên bảo hiểm xe từ một người nào
đó ta chỉ cần gọi lệnh sau:
String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(person -> person.getCar())
flatMap(car -> car.getInsurance())
map(insurance -> insurance.getName())
orElse("Unknown");
}
Hiểu đơn giản về hàm getCarInsuranceName trên như sau:
Câu lệnh flatMap(person -> person.getCar()) sẽ lấy tên Optional car từ lớp Optional person Tức là sau câu lệnh này ta sẽ được Optional car
Tương tự fatMap(car -> car.getInsurance()) ta sẽ có được Optional của Insurance
Và câu lệnh map(insurance -> insurance.getName()) ta sẽ có tên của bảo hiểm tương ứng với person nào đó
Nếu không có giá trị trả về thì nó cho ra kết quả “Unknown”
II Câu hỏi đưa ra.
Bài 1: Điểm khác biệt giữa phương thức map và flatMap trong Monadic Operations là gì
A. map để chuyển đổi một kiểu dữ liệu từ dạng này sang dạng khác còn flatMap thì không
Trang 7B. Nếu có phương thức f: TU thì trong “map” f: Optional<T>
Optional<U> còn trong “flatMap” f:T->Optional<U>
C. “map” thực chất là một hàm giúp cho ta biểu diễn ngắn gọn các biểu thức
sử dụng “flatmap”
D. Cả 3 ý trên đều đúng
Đáp án: B
Bài 2: Monadic Operation là gì?
A. Đây là tên một phương thức trong java 8
B. Đây là một Pattern mới trong java 8
C. Đây là một cách biểu diễn dữ liệu hỗ trợ cho người lập trình trong việc xử
lý các lỗi mà điển hình là NullPoiterExeption
D. Đây là một công cụ hỗ trợ trong việc lập trình java
Đáp án: C
Bài 3: Cho hàm sau:
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> f) {
return value == null ? EMPTY : f.apply(value);
}
Nếu không sử dụng flatMap mà thay vào đó là map thì ta phải viết lại hàm như thế nào
A. public<U> Optional<Optional<U>> map(Function<? super T, Optional<U>> f) { return value == null ? EMPTY : f.apply(value);
}
B. public<U> Optional<U> map(Function<? super T, Optional<U>> f) {
return value == null ? EMPTY : f.apply(value);
}
C. public<U> Optional<U> map(Function<? super T, U> f) {
return value == null ? EMPTY : f.apply(value);
}
D. public<U> Optional<U> map(Function<? super T, Optional<T>> f) {
return value == null ? EMPTY : f.apply(value);
}
Đáp án: A
Trang 8Tài liệu tham khảo
• Java SE 8 for the Really Impatient
• Monadic Java by Mario Fusco