อะไรคือจุดสำคัญของคลาสตัวเลือกของ Guava


89

ฉันเพิ่งอ่านเกี่ยวกับเรื่องนี้และเห็นคนที่ใช้คลาสนี้ แต่ในทุกกรณีการใช้งานnullจะได้ผลเช่นกัน - ถ้าไม่เข้าใจง่ายกว่านั้น ใครสามารถยกตัวอย่างที่เป็นรูปธรรมที่Optionalจะบรรลุสิ่งที่nullทำไม่ได้หรือในวิธีที่สะอาดกว่านี้ได้หรือไม่? สิ่งเดียวที่ฉันคิดได้คือใช้กับMapsที่ไม่ยอมรับnullคีย์ แต่ถึงอย่างนั้นก็ทำได้โดยใช้ "การแมป" ด้านข้างของค่า null ใครสามารถให้ข้อโต้แย้งที่น่าเชื่อถือกว่านี้แก่ฉันได้ไหม ขอขอบคุณ.


12
พูดจาโผงผางแบบสุ่ม: ฉันเกลียดเวลาที่มีคนใช้ "รูปแบบ" มากเกินไปและทำให้โค้ดดูน่าเกลียดเพื่อประโยชน์ทางทฤษฎีที่ไม่มีอยู่จริง ...
RAY

สำหรับ Java8 ฉันจะไม่ใช้คลาส Guava นี้อีกต่อไปเพราะมีประสิทธิภาพน้อยกว่า JDK ดูstackoverflow.com/a/10756992/82609
Sebastien Lorber

คำตอบ:


155

สมาชิกทีมฝรั่งนี่.

อาจเป็นข้อเสียที่ใหญ่ที่สุดเพียงข้อเดียวnullคือไม่ชัดเจนว่าควรหมายถึงอะไรในบริบทใด ๆ : ไม่มีชื่อที่เป็นภาพประกอบ ไม่ชัดเจนเสมอไปว่าnullหมายถึง "ไม่มีค่าสำหรับพารามิเตอร์นี้" ซึ่งเป็นค่าที่ส่งกลับบางครั้งหมายถึง "ข้อผิดพลาด" หรือแม้แต่ "ความสำเร็จ" (!!) หรือเพียงแค่ "คำตอบที่ถูกต้องคืออะไร" Optionalมักเป็นแนวคิดที่คุณหมายถึงเมื่อคุณทำให้ตัวแปรเป็นโมฆะ แต่ก็ไม่เสมอไป เมื่อไม่เป็นเช่นนั้นเราขอแนะนำให้คุณเขียนชั้นเรียนของคุณเองคล้ายกับOptionalแต่มีรูปแบบการตั้งชื่อที่แตกต่างกันเพื่อให้ชัดเจนว่าคุณหมายถึงอะไร

แต่ฉันจะบอกว่าข้อได้เปรียบที่ใหญ่ที่สุดของOptionalไม่ได้อยู่ในความสามารถในการอ่าน: ข้อดีคือการพิสูจน์ความงี่เง่า มันบังคับให้คุณคิดอย่างกระตือรือร้นเกี่ยวกับกรณีที่ไม่มีอยู่หากคุณต้องการให้โปรแกรมของคุณรวบรวมเลยเนื่องจากคุณต้องแกะOptionalและจัดการกับกรณีนั้นอย่างแข็งขัน Null ทำให้การลืมสิ่งต่างๆเป็นเรื่องง่ายอย่างไม่น่าเชื่อและแม้ว่า FindBugs จะช่วยได้ แต่ฉันก็ไม่คิดว่ามันจะช่วยแก้ปัญหาได้เช่นกัน สิ่งนี้มีความเกี่ยวข้องโดยเฉพาะอย่างยิ่งเมื่อคุณส่งคืนค่าที่อาจเป็น "ปัจจุบัน" หรือไม่ก็ได้ คุณ (และคนอื่น ๆ ) มีแนวโน้มที่จะลืมไปมากว่าother.method(a, b)สามารถคืนnullค่าได้มากกว่าที่คุณจะลืมซึ่งaอาจทำให้ผู้โทรลืมกรณีนั้นไม่ได้เนื่องจากพวกเขาต้องแกะวัตถุด้วยตัวเองnullother.methodเมื่อคุณกำลังดำเนินการ กลับมาOptional

ด้วยเหตุผลเหล่านี้เราขอแนะนำให้คุณใช้Optionalเป็นประเภทการส่งคืนสำหรับวิธีการของคุณ แต่ไม่จำเป็นต้องอยู่ในอาร์กิวเมนต์เมธอดของคุณ

(นี่คือเปลโดยสิ้นเชิงจากการสนทนาที่นี่ )


3
+1 สำหรับคำอธิบายที่ดี ฉันไม่รู้ว่านี่เป็นแรงบันดาลใจหรือเปล่า แต่ฉันจะเพิ่มตัวชี้ให้กับประเภทตัวเลือกใน SML, OCaml และ F # ซึ่งมีความหมายเดียวกันหลายตัว
Adam Mihalcin

2
ดีเสมอที่ได้รับคำตอบจากผู้ที่เกี่ยวข้องโดยตรง ฉันได้ +1 ไว้แค่นั้น ขออีก 1 คำตอบที่ดี (แต่ทำไม่ได้) ฉันคิดว่าประเด็นเกี่ยวกับการมีมันเป็นมูลค่าคืนเพื่อบังคับให้ผู้บริโภคใช้วิธีการที่ไม่คุ้นเคยให้พยายามยอมรับว่า "ไม่มีอะไร" ที่สามารถส่งคืนได้นั้นเป็นเหตุผลที่น่าสนใจอย่างหนึ่ง ฉันไม่ชอบแม้ว่ามันจะถูกใช้มากเกินไป (หรือแม้กระทั่งถูกทารุณกรรม) ก็ตาม เช่นฉันรู้สึกไม่สบายใจจริงๆเมื่อเห็นผู้คนใส่ Optionals เป็นค่านิยมใน Maps แผนที่การเก็บโมฆะสำหรับคีย์ที่ไม่มีอยู่จริง / ไม่ได้แมปเป็นกระบวนทัศน์ที่กำหนดไว้อย่างดี ... ไม่จำเป็นต้องทำให้ซับซ้อนมากขึ้น ...
RAY

9
มันเป็นอย่างดีเป็นที่ยอมรับว่าMapผลตอบแทนnullหากที่สำคัญเป็นแม็ป แต่จำได้ว่าถ้าคุณทำmap.put(key, null)แล้วmap.containsKey(key)จะกลับมาtrueแต่จะกลับมา map.get(key) สามารถเป็นประโยชน์ในการล้างความแตกต่างระหว่างกรณี "แมปอย่างชัดเจนกับค่าว่าง" และกรณี "ไม่ปรากฏในแผนที่" ฉันจะให้สิทธิ์ที่สามารถถูกล่วงละเมิดได้ แต่ฉันยังไม่มั่นใจว่ากรณีที่คุณอธิบายเป็นการละเมิด nullOptionalOptional
Louis Wasserman

8
ในการใช้การเปรียบเทียบแบบโบราณก็เคยมีสิ่งยักษ์เหล่านี้เรียกว่า "สมุดโทรศัพท์" :-) และถ้าฉันขอให้คุณค้นหาหมายเลขของใครบางคนและคุณบอกว่า "ไม่มีเบอร์สำหรับคนนั้น" ฉันจะพูด "คุณจะทำอย่างไร หมายความว่าพวกเขาอยู่ในนั้นด้วยหมายเลขที่ไม่อยู่ในรายการหรือคุณไม่พบรายการสำหรับพวกเขาเลย? " คำตอบที่เป็นไปได้ทั้งสองนั้นจับคู่กับ Optional.absent () และ null ตามลำดับ Optional.absent () คือ "positive negative" ขั้นสุดท้าย
Kevin Bourrillion

3
@RAY: ในทางหนึ่งOptional<T> คือค่า "พบ แต่ไม่ถูกต้อง" หรืออย่างแม่นยำมากขึ้นOptional<T>เป็นวิธีการตกแต่งประเภทใด ๆTด้วยค่า "พบ แต่ไม่ถูกต้อง" พิเศษ - การสร้างประเภทใหม่โดยการรวมสองประเภทที่มีอยู่ หากคุณมีร้อยชั้นเรียนก็จะได้รับยุ่งที่จะมีการสร้างแยกต่างหาก "พบ แต่ไม่ถูกต้อง" คุ้มค่าสำหรับแต่ละคน แต่Optional<T>สามารถทำงานสำหรับพวกเขาทั้งหมด
Daniel Pryden

9

ดูเหมือนว่าไฟล์ Maybeลายโมนาดจาก Haskell จริงๆ

คุณควรอ่านสิ่งต่อไปนี้ Wikipedia Monad (การเขียนโปรแกรมเชิงฟังก์ชัน) :

และอ่านFrom Optional to Monad with Guavaในบล็อกของ Kerflyn ซึ่งกล่าวถึงเรื่อง Optional of Guava ที่ใช้เป็น Monad:


แก้ไข: ด้วย Java8 มีตัวเลือกในตัวที่มีตัวดำเนินการ monadic เช่นflatMap. เรื่องนี้เป็นเรื่องที่ถกเถียงกัน แต่ในที่สุดก็มีการใช้

ดูhttp://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html

public Optional<String> tryFindSimilar(String s)  //...

Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar =       opt.flatMap(this::tryFindSimilar);

ตัวflatMapดำเนินการมีความสำคัญในการอนุญาตให้มีการดำเนินการแบบ monadic และอนุญาตให้เชื่อมโยงการเรียกที่ส่งคืนผลลัพธ์ทางเลือก

ลองคิดดูว่าถ้าคุณใช้mapโอเปอเรเตอร์ 5 ครั้งคุณจะจบลงด้วยการOptional<Optional<Optional<Optional<Optional<String>>>>>ใช้ตัวดำเนินการflatMapจะให้Optional<String>

เนื่องจาก Java8 ฉันไม่อยากใช้ Guava's Optional ซึ่งมีประสิทธิภาพน้อยกว่า


6

เหตุผลที่ดีอย่างหนึ่งที่จะใช้ก็คือมันทำให้ค่าว่างของคุณมีความหมายมาก แทนที่จะส่งคืนค่าว่างที่อาจมีความหมายหลายอย่าง (เช่นข้อผิดพลาดหรือความล้มเหลวหรือว่างเปล่า ฯลฯ ) คุณสามารถใส่ 'ชื่อ' เป็นค่าว่างได้ ดูตัวอย่างนี้:

ให้กำหนด POJO พื้นฐาน:

class PersonDetails {

String person;
String comments;

public PersonDetails(String person, String comments) {
    this.person = person;
    this.comments = comments;
}

public String getPerson() {
    return person;
}


public String getComments() {
    return comments;
}

}

ตอนนี้ให้ใช้ประโยชน์จาก POJO ง่ายๆนี้:

public Optional<PersonDetails> getPersonDetailstWithOptional () {

  PersonDetails details = null; /*details of the person are empty but to the caller this is meaningless,
  lets make the return value more meaningful*/


    if (details == null) {
      //return an absent here, caller can check for absent to signify details are not present
        return Optional.absent();
    } else {
      //else return the details wrapped in a guava 'optional'
        return Optional.of(details);   
    }
}

ตอนนี้ให้หลีกเลี่ยงการใช้ null และทำการตรวจสอบด้วยตัวเลือกเพื่อให้มีความหมาย

public void checkUsingOptional () {

    Optional<PersonDetails> details = getPersonDetailstWithOptional();

    /*below condition checks if persons details are present (notice we dont check if person details are null,
    we use something more meaningful. Guava optional forces this with the implementation)*/
    if (details.isPresent()) {

      PersonDetails details = details.get();

        // proceed with further processing
        logger.info(details);

    } else {
        // do nothing
        logger.info("object was null"); 
    }

    assertFalse(details.isPresent());
}

ดังนั้นในที่สุดมันก็เป็นวิธีที่จะทำให้โมฆะมีความหมายและมีความคลุมเครือน้อยลง


4

ข้อได้เปรียบที่สำคัญที่สุดของทางเลือกคือการเพิ่มรายละเอียดเพิ่มเติมให้กับสัญญาระหว่างผู้ใช้และผู้เรียกใช้ฟังก์ชัน ด้วยเหตุนี้จึงมีประโยชน์สำหรับพารามิเตอร์และประเภทการส่งคืน

หากคุณทำให้การประชุมมีอยู่เสมอOptionalสำหรับวัตถุว่างที่เป็นไปได้คุณจะเพิ่มคำชี้แจงเพิ่มเติมให้กับกรณีต่างๆเช่น

  1. Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)

    สัญญาระบุไว้อย่างชัดเจนว่ามีโอกาสที่ผลลัพธ์จะไม่ถูกส่งกลับ แต่ยังแสดงให้เห็นว่าจะใช้งานได้fromและtoไม่มีอยู่

  2. Optional<Integer> maxPrime(Optional<Integer> from, Integer to)

    สัญญาระบุว่า from เป็นทางเลือกดังนั้นค่าที่ไม่มีอยู่อาจมีความหมายพิเศษเช่น start from 2 ฉันคาดว่าค่า null สำหรับtoพารามิเตอร์จะทำให้เกิดข้อยกเว้น

ดังนั้นส่วนที่ดีของการใช้ตัวเลือกคือว่าสัญญาทั้งสองกลายเป็นพรรณนา (คล้ายกับ@NotNullคำอธิบายประกอบ) แต่ยังไม่เป็นทางการตั้งแต่คุณต้องเขียนโค้ดที่จะรับมือกับ.get()Optional


เหตุใดจึงต้องใช้ <List> ที่เป็นทางเลือกในเมื่อรายการว่างเปล่าจะสะอาดกว่า
RAY

ฉันเลือกตัวอย่างผิดในการกลับ ด้วยคอลเลกชันคุณสามารถทำให้แบบแผนส่งคืนคอลเล็กชันว่างแทนค่าว่างได้เสมอ หากใช้อนุสัญญานี้คุณไม่จำเป็นต้องมีทางเลือกสำหรับคอลเลกชัน ทางเลือกอาจถูกมองว่าเป็นคอลเลกชันที่มีศูนย์หรือองค์ประกอบเดียวดังนั้นจึงไม่จำเป็นต้องวางไว้รอบคอลเลกชันอื่น
raisercostin

2
ขึ้นอยู่กับบริบทอาจมีความแตกต่างอย่างมีนัยสำคัญระหว่างรายการที่มี 0 รายการและรายการที่ขาด
plasma147
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.