การใช้ Optional.ifPresent () อย่างเหมาะสม


99

ฉันพยายามเข้าใจifPresent()วิธีการของOptionalAPI ใน Java 8

ฉันมีตรรกะง่ายๆ:

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

แต่ส่งผลให้เกิดข้อผิดพลาดในการคอมไพล์:

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

แน่นอนฉันสามารถทำสิ่งนี้ได้:

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

แต่นี่ก็เหมือนกับการnullตรวจสอบที่รกรุงรัง

หากฉันเปลี่ยนรหัสเป็นสิ่งนี้:

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

รหัสเริ่มสกปรกขึ้นซึ่งทำให้ฉันคิดว่าจะกลับไปใช้nullเช็คแบบเก่า

ความคิดใด ๆ ?

คำตอบ:


160

Optional<User>.ifPresent()ใช้Consumer<? super User>เป็นอาร์กิวเมนต์ คุณกำลังส่งนิพจน์ที่ประเภทเป็นโมฆะ นั่นไม่ได้รวบรวม

ผู้บริโภคมีวัตถุประสงค์เพื่อนำไปใช้เป็นนิพจน์แลมบ์ดา:

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

หรือง่ายกว่าโดยใช้การอ้างอิงวิธีการ:

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

นี่คือสิ่งเดียวกับ

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

แนวคิดก็คือdoSomethingWithUser()การเรียกเมธอดจะดำเนินการก็ต่อเมื่อมีผู้ใช้อยู่ ifPresent()รหัสของคุณดำเนินการวิธีการเรียกโดยตรงและพยายามที่จะผ่านผลเป็นโมฆะในการ


2
รหัสนั้นเริ่มรก .. การตรวจสอบค่าว่างจะสะอาดกว่ามาก คุณคิดว่า doSomethingWithUser ไม่ใช่วิธีการคงที่หรือไม่
rayman

4
รหัสไหน? สิ่งที่คุณควรใช้คืออันที่สองซึ่งเรียกวิธีการอินสแตนซ์ (เช่นไม่คงที่) doSomethingWithUser () ไม่เห็นว่ามันเกะกะ รหัสสุดท้ายมีไว้เพื่ออธิบายคุณว่าเทียบเท่ากับแลมด้าในโลกก่อนแลมบ์ดา อย่าใช้มัน.
JB Nizet

2
ใช่ แต่คุณอาจคุ้นเคยกับคลาสที่ไม่ระบุตัวตนดังนั้นจึงเข้าใจว่าแลมบ์ดาทำอะไรโดยเห็นคลาสที่ไม่ระบุชื่อ นั่นคือประเด็น
JB Nizet

1
คุณไม่มีอะไรต้องแก้ไข ปล่อยให้มันเป็นเช่นนั้นและใช้ตัวอย่างที่สอง:user.ifPresent(this::doSomethingWithUser);
JB Nizet

11
@rayman หากคุณมีฟังก์ชันที่ส่งกลับOptional<User>มักไม่จำเป็นต้องเก็บไว้ในตัวแปรท้องถิ่น เพียงเชื่อมต่อวิธีการเรียก:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart Marks

21

นอกจากคำตอบของ @ JBNizet แล้วกรณีการใช้งานทั่วไปของฉันifPresentคือการรวม.isPresent()และ.get():

ทางเก่า:

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

วิธีการใหม่:

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

สำหรับฉันแล้วสิ่งนี้ง่ายกว่า


9

ทำไมต้องเขียนโค้ดที่ซับซ้อนในเมื่อคุณทำให้มันง่าย

อันที่จริงถ้าคุณจะใช้Optionalคลาสอย่างแน่นอนรหัสที่ง่ายที่สุดคือสิ่งที่คุณเขียนไปแล้ว ...

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

รหัสนี้มีข้อดีคือ

  1. อ่านได้
  2. ง่ายต่อการดีบัก (เบรกพอยต์)
  3. ไม่ยุ่งยาก

เพียงเพราะ Oracle ได้เพิ่มOptionalคลาสใน Java 8 ไม่ได้หมายความว่าจะต้องใช้คลาสนี้ในทุกสถานการณ์


1
ประโยชน์หลักของการใช้ ifPresent คือช่วยขจัดความจำเป็นที่คุณต้องโทรหา get () ด้วยตนเอง การโทร get () ด้วยตนเองนั้นเกิดข้อผิดพลาดได้ง่ายเนื่องจากง่ายต่อการลืมตรวจสอบ isPresent ก่อน แต่เป็นไปไม่ได้ที่คุณจะลืมถ้าคุณใช้ ifPresent
dustinroepsch

1
ตกลงและทุกครั้งที่คุณจะใช้ออบเจ็กต์ 'ผู้ใช้' คุณควรเรียก. ifPresent () โค้ดจะไม่สามารถอ่านได้อย่างรวดเร็วเพราะคุณจะอ่าน. ifPresent () นานเกินไป!
schlebe

2
สำหรับการแก้ไขการสะกดผิดในหน้าโปรไฟล์ของคุณ ( VB.Net , Netbeans , SqlServer , PostGresql , MySqlและ Linq คุณสามารถใช้บริการของฉันได้นอกจากนี้ยังมีรายการคำที่เกี่ยวข้องด้วย
Peter Mortensen

7

ใช้ flatMap หากมีค่าอยู่ flatMap จะส่งคืนสตรีมตามลำดับที่มีเฉพาะค่านั้นมิฉะนั้นจะส่งคืนสตรีมว่าง ifPresent()ดังนั้นจึงไม่มีความจำเป็นต้องใช้ ตัวอย่าง:

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

3
ไม่บังคับ :: สตรีมต้องการ java9
avmohan

7

คุณสามารถใช้การอ้างอิงวิธีการดังนี้:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

เมธอดifPresent()รับConsumerอ็อบเจกต์เป็น paremeter และ (จากJavaDoc ): "หากมีค่าอยู่ให้เรียกใช้ผู้บริโภคที่ระบุด้วยค่า" userราคามันเป็นตัวแปรของคุณ

หรือถ้าวิธีนี้doSomethingWithUserอยู่ในUserคลาสและไม่ใช่staticคุณสามารถใช้การอ้างอิงเมธอดดังนี้:

user.ifPresent(this::doSomethingWithUser);

1
แต่ doSomethingWithUser ไม่ใช่วิธีการคงที่หรือเป็นคลาส
rayman

@rayman โอเคถ้าไม่คงที่คุณสามารถทำได้เช่นนี้:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin

7
@AleksandrPodkutin คุณไม่ควรสร้างอินสแตนซ์ใหม่ของคลาสเพียงเพื่อเรียกใช้เมธอดเดียวจาก OP ดูเหมือนว่าเมธอดจะอยู่ในคลาสเดียวกับที่ถูกเรียกดังนั้นเขาควรใช้user.ifPresent(this::doSomethingWithUser);
Marv

@Marv ฉันไม่เห็นแบบฟอร์มยืนยันว่า OP อยู่ในคลาสเดียวกัน user.ifPresent(this::doSomethingWithUser);แต่ถ้าคุณมีความรู้สึกเช่นนั้นผมเห็นว่าเขาจะต้องใช้ ฉันจะเพิ่มคำตอบของฉัน
Aleksandr Podkutin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.