จะดีกว่าถ้าใช้ assert หรือ IllegalArgumentException สำหรับพารามิเตอร์เมธอดที่ต้องการ?


87

ใน Java ซึ่งแนะนำมากขึ้นและทำไม? ทั้งสองประเภทจะโยนข้อยกเว้นดังนั้นในการจัดการพวกเขาก็เหมือนกัน assertสั้นกว่านี้เล็กน้อย แต่ฉันไม่แน่ใจว่าสำคัญแค่ไหน

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

VS

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}

ฉันชอบแบบเรียบง่ายobj.hashCode()แทน ;-)
Marco

คำตอบ:


117

ระวัง!

การยืนยันจะถูกลบเมื่อรันไทม์เว้นแต่คุณจะระบุอย่างชัดเจนเพื่อ "เปิดใช้งานการยืนยัน" เมื่อรวบรวมรหัสของคุณ Java Assertions ไม่ควรใช้กับรหัสการผลิตและควร จำกัด วิธีการส่วนตัว (ดูข้อยกเว้นการยืนยัน ) เนื่องจากวิธีการส่วนตัวคาดว่าจะเป็นที่รู้จักและใช้งานโดยนักพัฒนาเท่านั้น นอกจากนี้assertจะโยนAssertionErrorที่ขยายErrorไม่ได้Exceptionและซึ่งโดยปกติจะระบุว่าคุณมีข้อผิดพลาดที่ผิดปกติมาก (เช่น "OutOfMemoryError" ซึ่งยากต่อการกู้คืนจากใช่หรือไม่) คุณไม่คาดว่าจะสามารถรักษาได้

ลบการตั้งค่าสถานะ "เปิดใช้งานการยืนยัน" และตรวจสอบกับดีบักเกอร์และคุณจะเห็นว่าคุณจะไม่เหยียบโทร IllegalArgumentException การโยน ... เนื่องจากรหัสนี้ยังไม่ได้รวบรวม (อีกครั้งเมื่อลบ "ea")

มันเป็นการดีกว่าที่จะใช้โครงสร้างที่สองสำหรับวิธีสาธารณะ / ที่ได้รับการป้องกันและถ้าคุณต้องการบางสิ่งที่ทำในรหัสบรรทัดเดียวมีอย่างน้อยหนึ่งวิธีที่ฉันรู้ ผมเองใช้ฤดูใบไม้ผลิกรอบของAssertชั้นเรียนที่มีไม่กี่วิธีในการตรวจสอบข้อโต้แย้งและโยน 'IllegalArgumentException' กับความล้มเหลว โดยทั่วไปสิ่งที่คุณทำคือ:

Assert.notNull(obj, "object was null");

... ซึ่งอันที่จริงแล้วจะรันรหัสเดียวกันกับที่คุณเขียนไว้ในตัวอย่างที่สองของคุณ มีไม่กี่วิธีที่มีประโยชน์อื่น ๆ เช่นhasText, hasLengthในการมี

ฉันไม่ชอบเขียนโค้ดเกินความจำเป็นฉันจึงมีความสุขเมื่อฉันลดจำนวนบรรทัดที่เขียนเป็น 2 (2 บรรทัด> 1 บรรทัด) :-)


อ่าฉันลืมเรื่องการยืนยันออกไป! คำตอบที่ดี ฉันจะรอสักครู่เพื่อดูว่าสิ่งอื่นเข้ามาแล้วยอมรับมัน :)
Daenyth

2
โปรดทราบว่าไม่มีการตั้งค่าสถานะที่เอาการยืนยันในเวลารวบรวม (แม้ว่าพวกเขาอาจถูกลบออกผ่านการทำตามเงื่อนไข ) การยืนยันถูกปิดการใช้งานโดยค่าเริ่มต้น (ฉันคิดว่า JVM ปฏิบัติต่อพวกเขาเป็น NOOP) แต่สามารถเปิดใช้งานผ่านทางjava -eaโปรแกรมได้ @ Jalayn ฉันคิดว่าการยืนยันในโค้ดการผลิตนั้นใช้ได้อย่างสมบูรณ์แบบเนื่องจากมันมีประโยชน์สำหรับการดีบักในฟิลด์
Justin Muller

@Jalayn, -1 คอมไพเลอร์ไม่ลบรหัสยืนยัน java -eaถึงแม้ว่าพวกเขาจะไม่ถูกเรียกใช้จนกว่าคุณจะทำคำสั่ง
Pacerier

5
ไม่จำเป็นต้องใช้เฟรมเวิร์กสปริงเมื่อคุณสามารถใช้งานได้Objects.requreNonNull
cambunctious

46

คุณต้องใช้ข้อยกเว้น การใช้การยืนยันจะเป็นการใช้คุณสมบัติผิดวัตถุประสงค์

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

ตัวอย่างเช่นการยืนยัน

assert myConnection.isConnected();

หมายความว่า "ฉันรู้ว่าแต่ละเส้นทางของรหัสที่นำไปสู่การยืนยันนี้ช่วยให้มั่นใจได้ว่าmyConnectionมีการเชื่อมต่อหากรหัสด้านบนไม่สามารถรับการเชื่อมต่อที่ถูกต้องได้ควรมีข้อยกเว้นหรือส่งคืนก่อนถึงจุดนี้"

ในทางกลับกันตรวจสอบ

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

หมายความว่า "การโทรเข้าห้องสมุดของฉันโดยไม่สร้างการเชื่อมต่อเป็นข้อผิดพลาดในการเขียนโปรแกรม"


1
ข้อมูลนี้มีประโยชน์จริง ๆ แต่ฉันยอมรับ Jalayn เนื่องจากชี้ให้เห็นว่าฉันสามารถแนะนำข้อบกพร่องด้วยวิธีการยืนยันได้อย่างไร
Daenyth

4
จุดที่ดีเยี่ยม "ข้อยกเว้นที่ไม่ได้ตรวจสอบได้รับการออกแบบมาเพื่อตรวจหาข้อผิดพลาดในการเขียนโปรแกรมของผู้ใช้ห้องสมุดของคุณในขณะที่การตรวจสอบยืนยันนั้นออกแบบมาเพื่อตรวจหาข้อผิดพลาดในตรรกะของคุณเอง
Asim Ghaffar

จริง แต่สิ่งนี้ถือว่า OP กำลังเขียนไลบรารี หากรหัสของพวกเขาเป็นเพียงสำหรับการใช้งานภายในยืนยันได้เป็นที่ยอมรับ
949300

11

หากคุณกำลังเขียนฟังก์ชั่นที่ไม่อนุญาตให้nullเป็นค่าพารามิเตอร์ที่ถูกต้องคุณควรเพิ่ม@Nonnullคำอธิบายประกอบให้กับลายเซ็นและใช้Objects.requireNonNullเพื่อตรวจสอบว่าข้อโต้แย้งนั้นเป็นnullและโยนNullPointerExceptionถ้ามันเป็น @Nonnullคำอธิบายประกอบสำหรับเอกสารและจะให้คำเตือนที่เป็นประโยชน์ที่รวบรวมเวลาในบางกรณี มันไม่ได้ป้องกันnullการถูกส่งผ่านขณะทำงาน

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

UPDATE:@Nonnullคำอธิบายประกอบไม่ได้เป็นส่วนหนึ่งของห้องสมุดมาตรฐาน Java มีมาตรฐานการแข่งขันจำนวนมากจากห้องสมุดบุคคลที่สาม (ดูที่ @NotNull Java หมายเหตุใดที่ฉันควรใช้? ) ไม่ได้หมายความว่ามันเป็นความคิดที่ดีที่จะใช้ แต่ก็ไม่ได้มาตรฐาน


ขอบคุณสำหรับการชี้ให้เห็นObjects.requireNonNull()ซึ่งเป็นของใหม่สำหรับฉัน คุณรู้หรือไม่ว่ามีวิธี "requireTrue ()" หรือ "requireEqual ()" ที่คล้ายกันทุกที่? ไม่มีอะไรเทียบกับสปริงยืนยัน แต่ไม่ใช่ทุกคนที่ใช้สิ่งนั้น
user949300

@ user949300 ใช้Objects.requireNonNull()สำหรับการตรวจสอบความถูกต้องของอาร์กิวเมนต์ หากมีข้อโต้แย้งที่จะต้องเป็นtrueหรือเท่ากับบางสิ่งบางอย่างแล้วอาร์กิวเมนต์นั้นไม่มีจุดหมาย สำหรับกรณีที่เกิดข้อผิดพลาดอื่น ๆ นอกเหนือจากข้อโต้แย้งที่ผิดกฎหมายคุณควรที่ถูกต้องมากขึ้นอธิบายข้อผิดพลาด นอกจากนี้ยังมี JUnit แต่สำหรับการทดสอบ throwExceptionAssert
cambunctious

ฉันคิดว่าอยากพูดฟังก์ชั่นสแควร์รูObjects.requireTrue(x >= 0.0);ทหรือแฮชObjects.requireEquals(hash.length == 256)
user949300

ฉันต้องใช้ @Nullull แบบใด javax.validation.constraints.NotNull?
Aguid

ฉันจะใช้คำอธิบายประกอบ Eclipse JDTเพราะพวกเขาทำโดยพวกที่ฉลาด :) เอกสารประกอบ: help.eclipse.org/neon/… - คุณยังสามารถกำหนดค่า IntelliJ เพื่อใช้งานได้
koppor

2

ฉันชอบที่จะโยน IllegalArgumentException มากกว่าการยืนยัน

การยืนยันส่วนใหญ่จะใช้ใน JUnit หรือเครื่องมือทดสอบอื่น ๆ เพื่อตรวจสอบ / ยืนยันผลการทดสอบ ดังนั้นอาจให้ความรู้สึกที่ผิดพลาดกับผู้พัฒนารายอื่นว่าวิธีการของคุณเป็นวิธีทดสอบ

นอกจากนี้ก็จะทำให้ความรู้สึกที่จะโยน IllegalArgumentException เมื่อวิธีการที่ได้รับการผ่านการโต้แย้งที่ผิดกฎหมายหรือไม่เหมาะสม สิ่งนี้สอดคล้องกับระเบียบการจัดการข้อยกเว้นมากขึ้นตามด้วยนักพัฒนา Java


5
คำยืนยันอยู่ที่ประมาณ 40 ปีก่อน JUnit - ถามโปรแกรมเมอร์ C เกี่ยวกับแมโคร ASSERT
JBRWilkinson

3
คำถามนี้ไม่เกี่ยวกับ c; มันเกี่ยวกับ Java ดังนั้นฉันจึงได้ตอบในบริบทของ Java
rai.skumar

อย่าสับสน assert (คำหลักที่สงวนไว้) เทียบกับ Assert (คลาส JUnit) พวกเขาทั้งคู่ใช้เพื่อตรวจสอบตัวแปร แต่นอกเหนือจากนั้นพวกเขาเป็นสองสิ่งที่แตกต่างกันมากและทำงานแตกต่างกันมาก
Newtopian

1

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


1

ฉันไม่ได้ใช้ aserts บ่อยนัก แต่ใช้วิธีทั่วไปกับ Lombock @NonNull: https://projectlombok.org/features/NonNull

การใช้งานลอมบอก: นำเข้าลอมบอก

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

รุ่น Java:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

ลอมบอกเป็นห้องสมุดที่ค่อนข้างดีซึ่งฉันใช้ทุกที่

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