ดึงค่าโดยไม่ต้องตรวจสอบเป็นโมฆะใน Java


15

หลายครั้งที่ฉันตรวจสอบตัวเองเป็นโมฆะเมื่อดึงค่าจากลำดับชั้นข้อมูลบางอย่างเพื่อหลีกเลี่ยง NullPointerExceptions ซึ่งฉันพบว่ามีแนวโน้มที่จะเกิดข้อผิดพลาดและต้องมีสำเร็จรูปจำนวนมาก

ฉันได้เขียนกิจวัตรที่ง่ายมากซึ่งทำให้ฉันสามารถข้ามการตรวจสอบค่าว่างเมื่อดึงวัตถุ ...

public final class NoNPE {

    public static <T> T get(NoNPEInterface<T> in) {
        try {
            return in.get();
        } catch (NullPointerException e) {
            return null;
        }
    }

    public interface NoNPEInterface<T> {
        T get();
    }
}

ฉันใช้มันแบบนี้ ...

Room room = NoNPE.get(() -> country.getTown().getHouses().get(0).getLivingRoom());

ด้านบนทำให้ฉันได้รับวัตถุห้องหรือโมฆะโดยไม่ต้องตรวจสอบระดับผู้ปกครองทั้งหมด

คุณคิดอย่างไรกับข้างต้น? ฉันกำลังสร้างรูปแบบที่มีปัญหาหรือไม่? มีวิธีที่ดีกว่านี้ในความคิดของคุณ?


1
เห็นได้ชัดว่าคุณใช้ Java 8 ฉันขอแนะนำให้คุณพิจารณาการออกแบบแอปพลิเคชันของคุณใหม่เพื่อใช้java.util.Optionalแทนค่า Null เพื่อแสดงข้อมูลที่ขาดหายไปหรือไม่? นี่เป็นเครื่องมือที่มีประโยชน์ทั้งสำหรับกรณีที่คุณอธิบายและกรณีที่คุณต้องการดำเนินการกับข้อมูลเริ่มต้นมากกว่าเพียงแค่ส่งคืนสภาพความล้มเหลวในตอนท้ายของห่วงโซ่ ..
Periata Breatta

ฉันคิดว่าคุณได้ค้นพบmonad Option(หรือMaybe) monad :) อีกครั้ง
Andres F.

อาจเป็นไปได้ที่จะส่งคืนทางเลือกแทน T หรือ null: วิธีนี้คุณสามารถใช้เมธอด orElse () ได้โดยตรง 18 เดือนต่อมา แต่สามารถช่วยใครบางคน
Benjas

อีกวิธีหนึ่งที่กล่าวถึงในโพสต์นี้legalargumentexception.blogspot.com/2015/03/…หนึ่งในนั้นใช้ห้องสมุดชื่อ kludje ซึ่งมีรูปแบบที่น่าสนใจมาก
Benj

คำตอบ:


13

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

ถ้าคุณใช้NonPEคลาสอย่างเต็มที่คุณจะมีปัญหาในการดีบักอย่างรุนแรง ฉันคิดว่าเป็นการดีกว่าที่จะทราบว่าโซ่ถูกทำลายไปกว่าการรับเสียงเงียบ ๆnullซึ่งอาจซ่อนข้อผิดพลาดได้ลึกกว่า

นอกจากนี้ละเมิดกฎหมายของ Demetercountry.getTown().getHouses().get(0).getLivingRoom() : บ่อยครั้งที่การละเมิดหลักการที่ดีบางอย่างทำให้คุณต้องใช้วิธีแก้ปัญหานอกรีตเพื่อแก้ไขปัญหาที่เกิดจากการละเมิดหลักการดังกล่าว

คำแนะนำของฉันคือคุณใช้มันด้วยความระมัดระวังและลองแก้ไขข้อบกพร่องในการออกแบบที่ทำให้คุณต้องเกิดขึ้นในรถไฟใต้ดินขบวนซากเรืออับปาง (ดังนั้นคุณไม่จำเป็นต้องใช้NonPEทุกที่) มิฉะนั้นคุณอาจมีข้อบกพร่องที่ยากต่อการตรวจจับ


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

โดยทั่วไปเมื่อใช้Optionmonad คุณจะไม่สนใจว่าในกรณีใดที่มูลค่าขาดหายไป Eitherเมื่อคุณทำเช่นการดูแลเกี่ยวกับเรื่องนี้คุณอาจจะใช้ชนิดที่แตกต่างกันเช่น
Andres F.

แนวทางของ OP คล้ายกับ C # 6 ?.และ?[]ตัวดำเนินการ ตัวอย่างหนึ่งของเมื่อคุณอาจต้องการใช้สิ่งนั้นคือการตั้งค่าฝั่งเซิร์ฟเวอร์ลำดับชั้น var shouldDoThing = settings?.a?.b?.c ?? defaultSetting;ใครสนใจว่าทำไมส่วนใดส่วนหนึ่งของมันจึงเป็นโมฆะ? บางทีคุณไม่สามารถเรียกการตั้งค่า บางทีคุณอาจตัดสินใจลบส่วนของการตั้งค่า ไม่ว่าในกรณีใดคุณไม่สามารถนับได้อย่างแท้จริงในการตั้งค่าเซิร์ฟเวอร์ดังนั้นค่าเริ่มต้นมักเป็นความคิดที่ดีและคุณไม่สนใจว่าทำไมคุณไม่สามารถรับการตั้งค่าที่แท้จริงได้เว้นแต่จะเกิดขึ้นบ่อยครั้งเมื่อไม่ควรทำ .
chris

settings.a.b.cตอนนี้ผมไม่ได้บอกว่าเป็นอย่างเคร่งครัดดีขึ้นหรือเลวร้ายยิ่งกว่าการแปลค่าเริ่มต้นและก็เดินทางกลับค่าที่คุณต้องการผ่านเข้าถึงปกติ จากนั้นอีกครั้งนี่เป็นตัวอย่างเดียวที่แยกได้
chris

10

ความคิดนั้นดีจริง ๆ แล้วดีมาก ตั้งแต่ Java 8 Optionalประเภทที่มีอยู่คำอธิบายรายละเอียดสามารถพบได้ที่ประเภท Java ตัวเลือก ตัวอย่างของสิ่งที่คุณโพสต์คือ

Optional.ofNullable(country)
    .map(Country::getTown)
    .map(Town::Houses);

และเพิ่มเติมเกี่ยวกับ


1
ใช่ฉันตระหนักถึงคลาสตัวเลือกจากทั้ง Java 8 และ Guava และพวกมันมีประโยชน์จริงๆ แต่คุณไม่สามารถดึงวัตถุได้ตามปกติแล้วคุณจะต้องทำให้โค้ดอ่านยากขึ้นและมีประสิทธิภาพน้อยลงเช่นกัน แต่ข้อเสียคือมีผู้ให้บริการที่มีประโยชน์มากมายที่คลาสเสริมมีให้
Eurig Jones

3
@EurigJones ฉันไม่คิดว่ารหัสจะมีประสิทธิภาพน้อยลง การอ่านในสายตาของคนดู แต่ฉันเถียงว่าOptionalเป็นวิธีแก้ปัญหาที่อ่านง่ายกว่าของทั้งสองถ้าเพียงเพราะ - ไม่เหมือนข้อเสนอของคุณ - มันเป็นสำนวนที่ใช้กันทั่วไปมาก มันกระชับยิ่งกว่าของคุณ!
Andres F.

0

วิธีการของคุณทำงานได้ดีพอสำหรับวัตถุประสงค์ที่ต้องการแม้ว่าจะส่งคืนnullเมื่อคุณได้รับNullPointerExceptionเสียงเช่นการออกแบบที่ไม่ดี

พยายามหลีกเลี่ยงnullเมื่อคุณสามารถและมีเพียงผ่านพวกเขาเมื่อพวกเขาเป็นตัวแทนของอะไรบางอย่างหรือมีความหมายพิเศษและมีเพียงพวกเขากลับมาเมื่อพวกเขาเป็นตัวแทน / สิ่งที่มีค่าเฉลี่ย - NullPointerExceptionมิฉะนั้นคุณควรโยน สิ่งนี้จะหลีกเลี่ยงข้อบกพร่องและความสับสน หากObjectไม่ควรจะnullเป็นNullPointerควรจะโยน ถ้าวัตถุnullนั้นไม่มีอะไรจะผิดพลาดเมื่อวัตถุถูกส่งเข้ามามิฉะนั้นวิธีการของคุณจะใช้ได้


0

ฉันรู้สึกถึงความเจ็บปวดของคุณ แต่วิธีแก้ปัญหาที่เสนอนั้นเป็นแนวคิดที่ไม่ดี

  • หากหนึ่งใน getters โยน NPE ด้วยเหตุผลอื่นคุณจะไม่สนใจมัน
  • แลมบ์ดานั้นมีความเสี่ยงที่จะเติบโตเป็นโค้ดที่น่ากลัว ตัวอย่างเช่นถ้ามีความต้องการที่จะกลับมาใหม่อย่างต่อเนื่องเป็นพิเศษเมื่อมีบ้านในเมืองไม่มีโปรแกรมเมอร์ขี้เกียจสามารถขยาย Lamda NoNPE.getทิ้งทุกอย่างที่อยู่ในห่อ
  • ตามที่ระบุไว้แล้วOptional.mapคือสิ่งที่คุณกำลังมองหา
  • บทลงโทษของการสร้างตัวอย่างใหม่ของ NullPointerException นั้นมีความสำคัญ เป็นไมโครวินาทีจำนวนมากโดยเฉพาะอย่างยิ่งเมื่อสแตกการโทรของคุณเริ่มใหญ่ขึ้น เป็นการยากที่จะคาดการณ์ว่ายูทิลิตี้ของคุณจะถูกใช้งานที่ไหน

ตามบันทึกข้าง, ซ้ำNoNPEInterfacejava.util.function.Supplier

ในบางกรณีคุณอาจพิจารณาใช้การประเมินผลนิพจน์ที่มีอยู่ในหลาย ๆ กรอบ (ตัวอย่าง: EL, SpEL):

evaluateProperty(country, "town.houses[0].livingRoom")

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