Sự khác biệt giữa hai phương pháp này là gì: Optional.flatMap()
và Optional.map()
?
Một ví dụ sẽ được đánh giá cao.
Stream#flatMap
và Optional#flatMap
.
Sự khác biệt giữa hai phương pháp này là gì: Optional.flatMap()
và Optional.map()
?
Một ví dụ sẽ được đánh giá cao.
Stream#flatMap
và Optional#flatMap
.
Câu trả lời:
Sử dụng map
nếu hàm trả về đối tượng bạn cần hoặc flatMap
nếu hàm trả về Optional
. Ví dụ:
public static void main(String[] args) {
Optional<String> s = Optional.of("input");
System.out.println(s.map(Test::getOutput));
System.out.println(s.flatMap(Test::getOutputOpt));
}
static String getOutput(String input) {
return input == null ? null : "output for " + input;
}
static Optional<String> getOutputOpt(String input) {
return input == null ? Optional.empty() : Optional.of("output for " + input);
}
Cả hai bản in đều in cùng một thứ.
[flat]Map
bao giờ gọi hàm ánh xạ với một input == null
? Sự hiểu biết của tôi là Optional
sắp xếp nếu nó vắng mặt - [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/iêu ) dường như sao lưu điều này - " Nếu có giá trị, hãy áp dụng .. . ".
Optional.of(null)
là một Exception
. Optional.ofNullable(null) == Optional.empty()
.
Cả hai đều có một chức năng từ loại tùy chọn đến một cái gì đó.
map()
áp dụng chức năng " như hiện tại " trên tùy chọn bạn có:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
Điều gì xảy ra nếu chức năng của bạn là một chức năng từ T -> Optional<U>
?
Kết quả của bạn bây giờ là một Optional<Optional<U>>
!
Đó là những gì flatMap()
về: nếu chức năng của bạn đã trả về một Optional
, flatMap()
thông minh hơn một chút và không gấp đôi nó, trả lại Optional<U>
.
Đó là thành phần của hai thành ngữ chức năng: map
và flatten
.
Lưu ý: - bên dưới là hình minh họa của chức năng bản đồ và bản đồ, nếu không, Tùy chọn chủ yếu được thiết kế để chỉ được sử dụng làm loại trả về.
Như bạn đã biết Tùy chọn là một loại thùng chứa có thể có hoặc không chứa một đối tượng, do đó, nó có thể được sử dụng bất cứ nơi nào bạn dự đoán giá trị null (Bạn có thể không bao giờ thấy NPE nếu sử dụng Tùy chọn đúng cách). Ví dụ: nếu bạn có một phương thức mong đợi một đối tượng người có thể là null, bạn có thể muốn viết phương thức như thế này:
void doSome(Optional<Person> person){
/*and here you want to retrieve some property phone out of person
you may write something like this:
*/
Optional<String> phone = person.map((p)->p.getPhone());
phone.ifPresent((ph)->dial(ph));
}
class Person{
private String phone;
//setter, getters
}
Ở đây bạn đã trả về một loại Chuỗi được tự động bọc trong một loại Tùy chọn.
Nếu lớp người trông như thế này, tức là điện thoại cũng là Tùy chọn
class Person{
private Optional<String> phone;
//setter,getter
}
Trong trường hợp này, chức năng gọi bản đồ sẽ bao bọc giá trị được trả về trong Tùy chọn và mang lại kết quả như:
Optional<Optional<String>>
//And you may want Optional<String> instead, here comes flatMap
void doSome(Optional<Person> person){
Optional<String> phone = person.flatMap((p)->p.getPhone());
phone.ifPresent((ph)->dial(ph));
}
PS; Không bao giờ gọi phương thức get (nếu bạn cần) trên Tùy chọn mà không kiểm tra phương thức đó bằng isPftime () trừ khi bạn không thể sống mà không có NullPulumExceptions.
Person
đang sử dụng sai Optional
. Việc sử dụng API cho Optional
các thành viên như thế này là trái với ý định - xem mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/
Điều giúp tôi là một cái nhìn vào mã nguồn của hai chức năng.
Bản đồ - bao bọc kết quả trong Tùy chọn.
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value)); //<--- wraps in an optional
}
}
FlatMap - trả về đối tượng 'thô'
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value)); //<--- returns 'raw' object
}
}
flatMap
"trả về đối tượng 'thô'? flatMap
cũng trả về đối tượng được ánh xạ "bọc" trong một Optional
. Sự khác biệt là trong trường hợp flatMap
, hàm ánh xạ bao bọc đối tượng được ánh xạ trong Optional
khi map
chính nó bao bọc đối tượng Optional
.
Optional.map()
:Lấy mọi phần tử và nếu giá trị tồn tại, nó được truyền cho hàm:
Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);
Bây giờ được thêm vào có một trong ba giá trị: true
hoặc false
được gói thành Tùy chọn , nếu optionalValue
có, hoặc Tùy chọn trống nếu không.
Nếu bạn không cần xử lý kết quả mà bạn có thể sử dụng ifPresent()
, thì nó không có giá trị trả về:
optionalValue.ifPresent(results::add);
Optional.flatMap()
:Hoạt động tương tự như cùng một phương pháp của luồng. Làm phẳng dòng suối. Với sự khác biệt là nếu giá trị được trình bày, nó được áp dụng cho hàm. Nếu không, một tùy chọn trống được trả lại.
Bạn có thể sử dụng nó để soạn các cuộc gọi hàm giá trị tùy chọn.
Giả sử chúng ta có các phương thức:
public static Optional<Double> inverse(Double x) {
return x == 0 ? Optional.empty() : Optional.of(1 / x);
}
public static Optional<Double> squareRoot(Double x) {
return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}
Sau đó, bạn có thể tính căn bậc hai của nghịch đảo, như:
Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);
hoặc, nếu bạn thích:
Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);
Nếu inverse()
hoặc squareRoot()
trả về Optional.empty()
, kết quả là trống rỗng.
Optional<Double>
kiểu như kiểu trả về.
Được chứ. Bạn chỉ cần sử dụng 'FlatMap' khi bạn phải đối mặt với các Tùy chọn lồng nhau . Đây là ví dụ.
public class Person {
private Optional<Car> optionalCar;
public Optional<Car> getOptionalCar() {
return optionalCar;
}
}
public class Car {
private Optional<Insurance> optionalInsurance;
public Optional<Insurance> getOptionalInsurance() {
return optionalInsurance;
}
}
public class Insurance {
private String name;
public String getName() {
return name;
}
}
public class Test {
// map cannot deal with nested Optionals
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.map(Car::getOptionalInsurance) // ① leads to a Optional<Optional<Insurance>
.map(Insurance::getName); // ②
}
}
Giống như Luồng, bản đồ # tùy chọn sẽ trả về giá trị được bao bọc bởi Tùy chọn. Đó là lý do tại sao chúng ta có một Tùy chọn lồng nhau - Optional<Optional<Insurance>
. Và tại, chúng tôi muốn ánh xạ nó như một ví dụ Bảo hiểm, đó là cách thảm kịch xảy ra. Các gốc được lồng tùy chọn. Nếu chúng ta có thể nhận được giá trị cốt lõi bất kể vỏ, chúng ta sẽ hoàn thành nó. Đó là những gì FlatMap làm.
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.flatMap(Car::getOptionalInsurance)
.map(Insurance::getName);
}
Cuối cùng, tôi đã hết sức giới thiệu Java 8 In Action cho bạn nếu bạn muốn nghiên cứu Java8 một cách có hệ thống.