Cách sử dụng phù hợp Optional.ifPresent ()


94

Tôi đang cố gắng hiểu ifPresent()phương thức của OptionalAPI trong Java 8.

Tôi có logic đơn giản:

Optional<User> user=...
user.ifPresent(doSomethingWithUser(user.get()));

Nhưng điều này dẫn đến lỗi biên dịch:

ifPresent(java.util.functionError:(186, 74) java: 'void' type not allowed here)

Tất nhiên tôi có thể làm điều gì đó như sau:

if(user.isPresent())
{
  doSomethingWithUser(user.get());
}

Nhưng điều này giống hệt như một nulltấm séc lộn xộn .

Nếu tôi thay đổi mã thành này:

 user.ifPresent(new Consumer<User>() {
            @Override public void accept(User user) {
                doSomethingWithUser(user.get());
            }
        });

Mã ngày càng bẩn khiến tôi nghĩ đến việc quay lại nullséc cũ .

Bất kỳ ý tưởng?

Câu trả lời:


154

Optional<User>.ifPresent()lấy một Consumer<? super User>đối số làm đối số. Bạn đang chuyển cho nó một biểu thức có kiểu là void. Vì vậy, điều đó không biên dịch.

Người tiêu dùng dự định được triển khai dưới dạng biểu thức lambda:

Optional<User> user = ...
user.ifPresent(theUser -> doSomethingWithUser(theUser));

Hoặc đơn giản hơn, sử dụng tham chiếu phương thức:

Optional<User> user = ...
user.ifPresent(this::doSomethingWithUser);

Về cơ bản, điều này giống với

Optional<User> user = ...
user.ifPresent(new Consumer<User>() {
    @Override
    public void accept(User theUser) {
        doSomethingWithUser(theUser);
    }
});

Ý tưởng là doSomethingWithUser()cuộc gọi phương thức sẽ chỉ được thực hiện nếu người dùng có mặt. Mã của bạn thực hiện lệnh gọi phương thức trực tiếp và cố gắng chuyển kết quả vô hiệu của nó cho ifPresent().


2
Mã đó đang trở nên lộn xộn .. kiểm tra rỗng sẽ sạch hơn nhiều. bạn không nghĩ sao? Điều đặc biệt là doSomethingWithUser không phải là một phương thức tĩnh
rayman

4
Mã nào? Phương thức bạn nên sử dụng là phương thức thứ hai, gọi phương thức thể hiện (tức là không tĩnh) doSomethingWithUser (). Tôi không thấy nó lộn xộn như thế nào. Đoạn mã cuối cùng ở đó để giải thích cho bạn sự tương đương với lambda trong thế giới tiền lambda. Đừng sử dụng nó.
JB Nizet

2
Có, nhưng bạn có thể quen với các lớp ẩn danh và do đó hiểu lambda làm gì bằng cách xem một lớp ẩn danh tương đương. Đó là điểm.
JB Nizet

1
Bạn không có gì để sửa đổi. Để nguyên như vậy và sử dụng ví dụ thứ hai:user.ifPresent(this::doSomethingWithUser);
JB Nizet

10
@rayman Nếu bạn có một hàm trả về Optional<User>, bạn thường không cần lưu nó trong một biến cục bộ. Chỉ cần chuỗi các cuộc gọi phương thức:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart Marks

19

Ngoài câu trả lời của @ JBNizet, trường hợp sử dụng chung của tôi ifPresentlà kết hợp .isPresent().get():

Cách cũ:

Optional opt = getIntOptional();
if(opt.isPresent()) {
    Integer value = opt.get();
    // do something with value
}

Cách mới:

Optional opt = getIntOptional();
opt.ifPresent(value -> {
    // do something with value
})

Điều này, đối với tôi, là trực quan hơn.


7

Sử dụng flatMap. Nếu có giá trị, flatMap trả về một Luồng tuần tự chỉ chứa giá trị đó, nếu không sẽ trả về một Luồng trống. Vì vậy không có nhu cầu sử dụng ifPresent(). Thí dụ:

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());

3
Tùy chọn :: phát trực tiếp cần java9
avmohan

7

Tại sao phải viết mã phức tạp khi bạn có thể làm cho nó đơn giản?

Thật vậy, nếu bạn hoàn toàn định sử dụng Optionallớp, mã đơn giản nhất là những gì bạn đã viết ...

if (user.isPresent())
{
    doSomethingWithUser(user.get());
}

Mã này có những ưu điểm là

  1. đọc được
  2. dễ gỡ lỗi (điểm ngắt)
  3. không khó

Chỉ vì Oracle đã thêm Optionallớp trong Java 8 không có nghĩa là lớp này phải được sử dụng trong mọi tình huống.


1
Lợi ích chính của việc sử dụng ifPresent là nó loại bỏ nhu cầu bạn phải gọi get () theo cách thủ công. Gọi get () bằng tay là dễ bị lỗi, vì nó rất dễ dàng để quên kiểm tra isPresent đầu tiên, nhưng nó không thể để bạn có thể quên nếu bạn sử dụng ifPresent
dustinroepsch

1
Ok và mỗi lần bạn sử dụng đối tượng 'user', bạn nên gọi .ifPresent (). Mã sẽ nhanh chóng trở nên khó đọc vì bạn sẽ đọc .ifPresent () quá nhiều thời gian!
schlebe

2
Để sửa lỗi chính tả trên trang hồ sơ của bạn ( VB.Net , Netbeans , SqlServer , PostGresql , MySql và Linq, bạn có thể sử dụng dịch vụ của tôi . Ngoài ra còn có một danh sách từ tương ứng .
Peter Mortensen

7

Bạn có thể sử dụng tham chiếu phương pháp như sau:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

Phương thức ifPresent()lấy Consumerđối tượng dưới dạng paremeter và (từ JavaDoc ): "Nếu có giá trị, hãy gọi người tiêu dùng được chỉ định với giá trị." Giá trị nó là biến của bạn user.

Hoặc nếu phương thức doSomethingWithUsernày nằm trong Userlớp và không phải static, bạn có thể sử dụng tham chiếu phương thức như sau:

user.ifPresent(this::doSomethingWithUser);

1
Nhưng doSomethingWithUser không phải là một phương thức tĩnh cũng không phải là một lớp.
rayman

@rayman Ok, nếu không tĩnh, bạn có thể làm như thế này:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin

7
@AleksandrPodkutin bạn không nên tạo ra một thể hiện mới của lớp chỉ để chạy một phương pháp, từ OP có vẻ như phương pháp này là trong lớp giống như nó đang được gọi là từ, do đó ông nên sử dụnguser.ifPresent(this::doSomethingWithUser);
Marv

@Marv Tôi không thấy bất kỳ mẫu khẳng định OP nào rằng nó ở cùng một lớp. Nhưng nếu bạn có cảm xúc như vậy, tôi đồng ý rằng anh ấy phải sử dụng user.ifPresent(this::doSomethingWithUser);. Tôi sẽ thêm nó vào câu trả lời của tôi.
Aleksandr Podkutin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.