คำหลักยืนยัน Java ทำอะไรและควรใช้เมื่อใด


601

ตัวอย่างชีวิตจริงอะไรบ้างที่จะเข้าใจบทบาทสำคัญของการยืนยัน


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

35
@Yishai: "คุณต้องคิด ... ยืนยันถูกปิด" ถ้าคุณต้องทำอย่างนั้นคุณจะทำผิด "การยืนยันเป็นการปรับให้เหมาะสมก่อนกำหนดของการใช้อย่าง จำกัด "นี่ค่อนข้างออกนอกเส้นทางไปแล้ว นี่คือสิ่งที่ซันใช้ในเรื่องนี้: " การใช้การยืนยันในเทคโนโลยี Java " และนี่ก็เป็นสิ่งที่ดีในการอ่าน: " ประโยชน์ของการเขียนโปรแกรมด้วยการยืนยัน (หรือที่เรียกว่าการยืนยันข้อความ) "
David Tonhofer

5
@DavidTonhofer ในชีวิตจริงคุณแทบไม่เคยเห็นเลย นี่พิสูจน์ได้ ตรวจสอบโครงการโอเพนซอร์สมากมายเท่าที่คุณต้องการ ฉันไม่ได้บอกว่าคุณไม่ตรวจสอบค่าคงที่ นั่นไม่ใช่สิ่งเดียวกัน ใส่อีกวิธี หากการยืนยันมีความสำคัญมากเหตุใดจึงปิดตามค่าเริ่มต้น
Yishai

17
การอ้างอิง FWIW: ความสัมพันธ์ระหว่างการยืนยันซอฟต์แวร์และคุณภาพของรหัส : "เรายังเปรียบเทียบประสิทธิภาพของการยืนยันกับเทคนิคการค้นหาข้อผิดพลาดยอดนิยมเช่นเครื่องมือวิเคราะห์แบบคงที่ของซอร์สโค้ดเราสังเกตจากกรณีศึกษาของเราที่เพิ่มความหนาแน่นของการยืนยัน ในไฟล์มีความหนาแน่นความผิดปกติลดลงอย่างมีนัยสำคัญทางสถิติ "
David Tonhofer

4
@DavidTonhofer David ฉันคิดว่าความรักที่คุณมีต่อการยืนยันนั้นเป็นประเภทของการเขียนโปรแกรมที่เฉพาะเจาะจงที่พวกคุณกำลังทำอยู่ในสาขาของฉันซึ่งทำงานกับเว็บแอปพลิเคชั่นที่ออกจากโปรแกรมด้วยเหตุผลใดก็ตาม ใช้ยืนยันนอกเหนือจากการทดสอบหน่วย /
จำนวนเต็ม

คำตอบ:


426

การยืนยัน (โดยวิธีของคีย์เวิร์ดassert ) ถูกเพิ่มใน Java 1.4 พวกเขาจะใช้ในการตรวจสอบความถูกต้องของค่าคงที่ในรหัส พวกเขาไม่ควรถูกเรียกใช้ในรหัสการผลิตและบ่งบอกถึงข้อผิดพลาดหรือการใช้ผิดเส้นทางของรหัส พวกเขาสามารถเปิดใช้งานในเวลาทำงานโดยวิธีการของ-eaตัวเลือกในjavaคำสั่ง แต่ไม่ได้เปิดตามค่าเริ่มต้น

ตัวอย่าง:

public Foo acquireFoo(int id) {
  Foo result = null;
  if (id > 50) {
    result = fooService.read(id);
  } else {
    result = new Foo(id);
  }
  assert result != null;

  return result;
}

71
อันที่จริงแล้ว Oracle บอกให้คุณไม่ใช้assertเพื่อตรวจสอบพารามิเตอร์วิธีสาธารณะ ( docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html ) ที่ควรจะโยนExceptionแทนที่จะฆ่าโปรแกรม
SJuan76

10
แต่คุณยังไม่ได้อธิบายว่าทำไมจึงมีอยู่ ทำไมคุณไม่สามารถทำ if () ตรวจสอบและโยนข้อยกเว้นได้?
El Mac

7
@ElMac - การยืนยันสำหรับส่วน dev / debug / test ของรอบ - พวกเขาไม่ได้มีไว้สำหรับการผลิต ถ้า if block ทำงานใน prod คำยืนยันง่ายๆจะไม่ทำให้ธนาคารแตก แต่การยืนยันที่มีราคาแพงซึ่งทำการตรวจสอบข้อมูลที่ซับซ้อนอาจทำให้สภาพแวดล้อมการผลิตของคุณลดลง
hoodaticus

2
@hoodaticus คุณหมายถึงความจริงที่ว่าฉันสามารถเปิด / ปิดการยืนยันทั้งหมดสำหรับรหัสผลิตภัณฑ์เป็นเหตุผลได้หรือไม่ เพราะฉันสามารถทำการตรวจสอบข้อมูลที่ซับซ้อนต่อไปแล้วจัดการกับข้อยกเว้น ถ้าฉันมีรหัสการผลิตฉันสามารถปิดการยืนยันที่ซับซ้อน (และอาจมีราคาแพง) เพราะมันควรจะทำงานและผ่านการทดสอบแล้ว? ในทางทฤษฎีแล้วพวกเขาไม่ควรนำโปรแกรมลงเพราะคุณจะมีปัญหาอยู่ดี
El Mac

8
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError. docs.oracle.com/javase/8/docs/technotes/guides/language/ ......
Bakhshi

325

สมมติว่าคุณควรจะเขียนโปรแกรมเพื่อควบคุมโรงไฟฟ้านิวเคลียร์ เป็นที่ชัดเจนว่าแม้ความผิดพลาดเล็กน้อยที่สุดอาจมีผลลัพธ์ที่เป็นความหายนะดังนั้นโค้ดของคุณจะต้องไม่มีข้อผิดพลาด (สมมติว่า JVM ปราศจากข้อผิดพลาดเพื่อการโต้แย้ง)

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

แนวคิดนี้ขึ้นอยู่กับกระบวนทัศน์การออกแบบโดยสัญญา (DbC): ก่อนอื่นคุณต้องกำหนด (ด้วยความแม่นยำทางคณิตศาสตร์) สิ่งที่วิธีการของคุณควรจะทำแล้วตรวจสอบโดยการทดสอบในระหว่างการดำเนินการจริง ตัวอย่าง:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
  return a + b;
}

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

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
    assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
  final int result = a + b;
    assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
  return result;
}

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

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

สิ่งนี้ยังไม่รับประกันสำหรับรหัสที่ปราศจากข้อบกพร่อง แต่มันใกล้กว่านั้นมากกว่าโปรแกรมปกติ


29
ฉันเลือกตัวอย่างนี้เพราะมันนำเสนอข้อบกพร่องที่ซ่อนอยู่ในรหัสปราศจากข้อผิดพลาดที่ดูเหมือนดีมาก หากสิ่งนี้คล้ายกับสิ่งที่คนอื่นนำเสนอพวกเขาอาจมีความคิดเดียวกันในใจ ;)
TwoThe

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

5
จำเป็นในกรณีง่าย ๆ นี้: ไม่ แต่ DbC กำหนดว่าจะต้องตรวจสอบทุกผลลัพธ์ ลองนึกภาพใครบางคนตอนนี้แก้ไขฟังก์ชั่นนั้นเป็นสิ่งที่ซับซ้อนมากขึ้นจากนั้นเขาก็ต้องปรับการตรวจสอบหลังและจากนั้นมันก็มีประโยชน์
TwoThe

4
ขออภัยที่จะฟื้นคืนชีพนี้ แต่ฉันมีคำถามเฉพาะ อะไรคือความแตกต่างระหว่างสิ่งที่ @TwoThe ทำและแทนที่จะใช้ assert เพียงแค่ทำการnew IllegalArgumentExceptionส่งต่อข้อความ? ฉันหมายถึงนอกเหนือจากการมี o เพิ่มthrowsการประกาศวิธีการและรหัสเพื่อจัดการข้อยกเว้นที่อื่น เหตุใดจึงต้องassertทิ้งการยกเว้นใหม่ หรือทำไมไม่ได้เป็นifแทนassert? ไม่สามารถรับสิ่งนี้ได้ :(
Blueriver

14
-1: การยืนยันการตรวจสอบโอเวอร์โฟลว์นั้นผิดถ้าaสามารถลบได้ การยืนยันครั้งที่สองนั้นไร้ประโยชน์ สำหรับค่า int จะเป็นกรณีที่ a + b - b == a การทดสอบนั้นจะล้มเหลวก็ต่อเมื่อคอมพิวเตอร์เสียขั้นพื้นฐานเท่านั้น เพื่อป้องกันสิ่งที่อาจเกิดขึ้นคุณต้องตรวจสอบความสอดคล้องของ CPU หลายตัว
วินไคลน์

63

การยืนยันเป็นเครื่องมือในการพัฒนาเพื่อดักจับบั๊กในโค้ดของคุณ พวกมันถูกออกแบบมาให้ลบออกได้ง่ายดังนั้นจึงไม่มีอยู่ในรหัสการผลิต ดังนั้นการยืนยันไม่ได้เป็นส่วนหนึ่งของ "โซลูชัน" ที่คุณส่งมอบให้กับลูกค้า เป็นการตรวจสอบภายในเพื่อให้แน่ใจว่าสมมติฐานที่คุณทำนั้นถูกต้อง ตัวอย่างที่พบบ่อยที่สุดคือการทดสอบหาค่าว่าง มีวิธีการมากมายที่เขียนเช่นนี้:

void doSomething(Widget widget) {
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

บ่อยครั้งในวิธีเช่นนี้วิดเจ็ตไม่ควรเป็นโมฆะ ดังนั้นถ้ามันเป็นโมฆะมีข้อผิดพลาดในรหัสของคุณที่ไหนสักแห่งที่คุณต้องติดตาม แต่โค้ดด้านบนจะไม่บอกคุณ ดังนั้นในความพยายามอย่างดีที่จะเขียนรหัส "ปลอดภัย" คุณยังซ่อนข้อบกพร่องด้วย มันดีกว่าที่จะเขียนโค้ดแบบนี้:

/**
 * @param Widget widget Should never be null
 */
void doSomething(Widget widget) {
  assert widget != null;
  widget.someMethod(); // ...
    ... // do more stuff with this widget
}

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

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

void doSomething(Widget widget) {
  assert widget != null;
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

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

นี่คือตัวอย่างในโลกแห่งความจริง: ฉันเคยเขียนวิธีที่เปรียบเทียบสองค่าโดยพลการเพื่อความเท่าเทียมกันโดยที่ค่าใดค่าหนึ่งอาจเป็นโมฆะ:

/**
 * Compare two values using equals(), after checking for null.
 * @param thisValue (may be null)
 * @param otherValue (may be null)
 * @return True if they are both null or if equals() returns true
 */
public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = thisValue.equals(otherValue);
  }
  return result;
}

รหัสนี้มอบหมายงานของequals()วิธีการในกรณีที่ค่านี้ไม่เป็นโมฆะ แต่จะถือว่าequals()วิธีการปฏิบัติตามสัญญาequals()อย่างถูกต้องโดยการจัดการพารามิเตอร์ว่าง

เพื่อนร่วมงานคัดค้านรหัสของฉันบอกฉันว่าหลายชั้นเรียนของเรามีequals()วิธีการบั๊กกี้ที่ไม่ได้ทดสอบว่าเป็นโมฆะดังนั้นฉันควรนำการตรวจสอบนั้นมาใช้ในวิธีนี้ เป็นที่ถกเถียงกันว่าเป็นสิ่งที่ฉลาดหรือหากเราควรบังคับให้เกิดข้อผิดพลาดดังนั้นเราจึงสามารถตรวจพบและแก้ไขได้ แต่ฉันรอการอนุมัติจากเพื่อนร่วมงานของฉันและทำการตรวจสอบว่างซึ่งฉันได้ทำเครื่องหมายไว้ด้วยความคิดเห็น:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = otherValue != null && thisValue.equals(otherValue); // questionable null check
  }
  return result;
}

การตรวจสอบเพิ่มเติมที่นี่other != nullมีความจำเป็นหากequals()วิธีการล้มเหลวในการตรวจสอบเป็นโมฆะตามที่กำหนดไว้ในสัญญา

แทนที่จะมีส่วนร่วมในการอภิปรายที่ไร้ผลกับเพื่อนร่วมงานของฉันเกี่ยวกับภูมิปัญญาของการให้รหัสรถ buggy อยู่ในฐานรหัสของเราฉันเพียงแค่ใส่สองยืนยันในรหัส การยืนยันเหล่านี้จะแจ้งให้เราทราบในระหว่างขั้นตอนการพัฒนาหากหนึ่งในคลาสของเราไม่สามารถใช้งานequals()ได้อย่างถูกต้องดังนั้นฉันสามารถแก้ไขได้:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
    assert otherValue == null || otherValue.equals(null) == false;
  } else {
    result = otherValue != null && thisValue.equals(otherValue);
    assert thisValue.equals(null) == false;
  }
  return result;
}

ประเด็นสำคัญที่ควรคำนึงถึงคือ:

  1. การยืนยันเป็นเครื่องมือในการพัฒนาเท่านั้น

  2. จุดการยืนยันคือการแจ้งให้คุณทราบหากมีข้อผิดพลาดที่ไม่เพียง แต่ในรหัสของคุณ แต่คุณในฐานรหัส (การยืนยันที่นี่จะทำเครื่องหมายข้อบกพร่องในคลาสอื่น ๆ จริง ๆ )

  3. แม้ว่าเพื่อนร่วมงานของฉันจะมั่นใจว่าชั้นเรียนของเราเขียนอย่างถูกต้องการยืนยันที่นี่จะยังคงมีประโยชน์ คลาสใหม่จะถูกเพิ่มที่อาจล้มเหลวในการทดสอบเป็นโมฆะและวิธีนี้สามารถตั้งค่าสถานะข้อบกพร่องเหล่านั้นสำหรับเรา

  4. ในการพัฒนาคุณควรเปิดใช้การยืนยันแม้ว่ารหัสที่คุณเขียนไม่ได้ใช้การยืนยัน IDE ของฉันถูกตั้งค่าให้ทำเช่นนี้โดยค่าเริ่มต้นสำหรับการปฏิบัติการใหม่ใด ๆ

  5. คำยืนยันไม่เปลี่ยนพฤติกรรมของรหัสในการผลิตดังนั้นเพื่อนร่วมงานของฉันมีความสุขที่การตรวจสอบว่างอยู่ที่นั่นและวิธีการนี้จะทำงานได้อย่างถูกต้องแม้ว่าequals()วิธีการนี้จะบั๊ก ฉันมีความสุขเพราะฉันจะequals()ใช้วิธีการบั๊กกี้ในการพัฒนา

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


ข้อดีของการซ่อนบั๊กคือการซ่อนข้อบกพร่องระหว่างการพัฒนา!
สูงศักดิ์

ไม่มีการตรวจสอบเหล่านี้ช้าจึงไม่มีเหตุผลที่จะปิดการผลิต ควรแปลงเป็นคำสั่งบันทึกเพื่อให้คุณสามารถตรวจพบปัญหาที่ไม่ปรากฏใน "ขั้นตอนการพัฒนา" (จริงๆแล้วมันไม่มีขั้นตอนการพัฒนาอยู่ดีการพัฒนาสิ้นสุดลงเมื่อคุณตัดสินใจหยุดการบำรุงรักษาโค้ดของคุณเลย)
Aleksandr Dubinsky

20

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

คำตอบ: แทบไม่เคยเลย

การยืนยันเป็นแนวคิดที่ยอดเยี่ยม รหัสที่ดีมีข้อความมากมายif (...) throw ...(และญาติของพวกเขาชอบObjects.requireNonNullและMath.addExact) อย่างไรก็ตามการตัดสินใจการออกแบบบางอย่างได้ จำกัด ยูทิลิตี้ของassert คำหลักอย่างมาก

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

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

ดังนั้นการใช้ควรเป็นที่ต้องการเช่นเดียวกับที่มันเป็นสิ่งจำเป็นสำหรับการตรวจสอบค่าพารามิเตอร์ของวิธีการสาธารณะและสำหรับการขว้างปาif (...) throw ...IllegalArgumentException

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

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

(Rant: JVM devs เป็นกลุ่มของตัวแปลงสัญญาณที่น่ากลัวและปรับให้เหมาะสมก่อนกำหนดนั่นคือเหตุผลที่คุณได้ยินเกี่ยวกับปัญหาด้านความปลอดภัยจำนวนมากในปลั๊กอิน Java และ JVM พวกเขาปฏิเสธที่จะรวมการตรวจสอบขั้นพื้นฐานและรหัสยืนยันในรหัสการผลิต จ่ายราคา)


2
@aberglas catch (Throwable t)จับทุกข้อคือ ไม่มีเหตุผลที่จะไม่พยายามดักบันทึกหรือลองใหม่ / กู้คืนจาก OutOfMemoryError, AssertionError ฯลฯ
Aleksandr Dubinsky

1
ฉันได้พบและกู้คืนจาก OutOfMemoryError
MiguelMunoz

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

2
@MiguelMunoz ในคำตอบของฉันฉันบอกว่าแนวคิดการยืนยันดีมาก มันคือการใช้งานของassertคำหลักที่ไม่ดี ฉันจะแก้ไขคำตอบของฉันเพื่อให้ชัดเจนยิ่งขึ้นว่าฉันอ้างอิงถึงคำหลักไม่ใช่แนวคิด
Aleksandr Dubinsky

2
ฉันชอบความจริงที่ว่ามันโยน AssertionError แทนที่จะเป็นข้อยกเว้น นักพัฒนาซอฟต์แวร์จำนวนมากยังไม่ได้เรียนรู้ว่าพวกเขาไม่ควรรับข้อยกเว้นถ้ารหัสสามารถโยนบางอย่างเช่น IOException ฉันมีข้อบกพร่องในรหัสของฉันถูกกลืนอย่างสมบูรณ์เพราะมีคนจับข้อยกเว้น ยืนยันไม่ได้ติดกับดักนี้ ข้อยกเว้นสำหรับสถานการณ์ที่คุณคาดว่าจะเห็นในรหัสการผลิต สำหรับการบันทึกคุณควรบันทึกข้อผิดพลาดทั้งหมดด้วยแม้ว่าข้อผิดพลาดจะเกิดขึ้นน้อย ตัวอย่างเช่นคุณต้องการให้การส่งผ่าน OutOfMemoryError โดยไม่ต้องบันทึกหรือไม่
MiguelMunoz

14

นี่คือกรณีการใช้งานที่พบบ่อยที่สุด สมมติว่าคุณเปลี่ยนค่า enum:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
}

ตราบใดที่คุณจัดการกับทุกกรณีคุณก็สบายดี แต่สักวันใครบางคนจะเพิ่มรูปใน enum ของคุณและลืมที่จะเพิ่มลงในคำสั่งเปลี่ยนของคุณ สิ่งนี้จะสร้างบั๊กที่อาจจับได้ยากเพราะเอฟเฟกต์จะไม่รู้สึกจนกว่าคุณจะออกจากคำสั่งสวิตช์ แต่ถ้าคุณเขียนสวิตช์แบบนี้คุณสามารถจับได้ทันที:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
  default:
    assert false : "Missing enum value: " + fruit;
}

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

9
เหตุใดจึงต้องใช้การยืนยันที่นี่แทนที่จะเป็นข้อยกเว้นบางประเภทเช่นข้อโต้แย้งที่ผิดกฎหมาย
liltitus27

4
สิ่งนี้จะเป็นการโยนAssertionErrorถ้าเปิดใช้งานการยืนยัน ( -ea) พฤติกรรมที่ต้องการในการผลิตคืออะไร? ภัยเงียบแบบไม่มี op และความเป็นไปได้ที่จะเกิดขึ้นภายหลังในการดำเนินการ อาจจะไม่. throw new AssertionError("Missing enum value: " + fruit);ฉันขอแนะนำให้ชัดเจน
aioobe

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

ดูเหมือนว่าจะผิด IMHO คุณไม่ควรใช้defaultเพื่อให้คอมไพเลอร์สามารถเตือนคุณในกรณีที่ขาดหายไป คุณสามารถreturnแทนbreak(ซึ่งอาจต้องมีการแยกวิธีการ) แล้วจัดการกรณีที่ขาดหายไปหลังจากเปลี่ยน assertวิธีนี้คุณจะได้รับทั้งคำเตือนและโอกาสต่อไปยัง
maaartinus

12

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

ตัวอย่างของการยืนยันอาจเป็นการตรวจสอบว่ามีการเรียกวิธีการเฉพาะกลุ่มตามลำดับที่ถูกต้อง (เช่นที่hasNext()ถูกเรียกมาก่อนnext()ในIterator)


1
คุณไม่ต้องโทร hasNext () ก่อนหน้าถัดไป ()
DJClayworth

6
@DJClayworth: คุณไม่จำเป็นต้องหลีกเลี่ยงการเรียกใช้การยืนยันเช่นกัน :-)
Donal Fellows

8

คำหลักยืนยันใน Java ทำอะไร

ลองดูโค้ดไบต์ที่คอมไพล์แล้ว

เราจะสรุปได้ว่า:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

สร้างรหัสไบต์เดียวกันเกือบแน่นอนเป็น:

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

ที่Assert.class.desiredAssertionStatus()เป็นtrueเมื่อ-eaมีการส่งผ่านในบรรทัดคำสั่งและเท็จอย่างอื่น

เราใช้System.currentTimeMillis()เพื่อให้แน่ใจว่าจะไม่ได้รับการเพิ่มประสิทธิภาพ ( assert true;ทำ)

ฟิลด์สังเคราะห์ถูกสร้างขึ้นเพื่อให้ Java จำเป็นต้องโทรAssert.class.desiredAssertionStatus()เพียงครั้งเดียวในขณะโหลดและจากนั้นแคชจะเก็บผลลัพธ์ไว้ที่นั่น ดูเพิ่มเติม: ความหมายของ "คงสังเคราะห์" คืออะไร?

เราสามารถตรวจสอบได้ด้วย:

javac Assert.java
javap -c -constants -private -verbose Assert.class

ด้วย Oracle JDK 1.8.0_45 ฟิลด์สแตติกสังเคราะห์ถูกสร้างขึ้น (ดูเพิ่มเติมที่: ความหมายของ "สแตติกสังเคราะห์" คืออะไร ):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

พร้อมกับ initializer คงที่:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

และวิธีการหลักคือ:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

เราสรุปได้ว่า:

  • ไม่มีการสนับสนุนระดับ bytecode สำหรับassert: มันเป็นแนวคิดภาษาจาวา
  • assertจะได้รับการเทิดทูนสวยดีด้วยคุณสมบัติของระบบ-Pcom.me.assert=trueที่จะมาแทนที่ในบรรทัดคำสั่งและ-eathrow new AssertionError()

2
ดังนั้นcatch (Throwable t)ข้อสามารถที่จะจับการละเมิดการยืนยันด้วยหรือไม่ สำหรับฉันที่ จำกัด ยูทิลิตี้ของพวกเขาเฉพาะกับกรณีที่เนื้อหาของการยืนยันใช้เวลานานซึ่งหายาก
Evgeni Sergeev

1
ฉันไม่แน่ใจว่าทำไมมัน จำกัด ประโยชน์ของการยืนยัน คุณไม่ควรจับ Throwable ได้ยกเว้นในบางกรณีที่หายากมาก หากคุณต้องการที่จะจับได้ Throwable แต่ต้องการที่จะไม่ได้รับการยืนยันคุณเพียงแค่จับAssertionErrorแรกและ rethrow
MiguelMunoz

7

ตัวอย่างโลกแห่งความจริงจากสแต็กคลาส (จากAssertion ในบทความ Java )

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}

80
สิ่งนี้จะถูกทำให้ขมวดคิ้วใน C: การยืนยันเป็นสิ่งที่ไม่ควรเกิดขึ้นจริง ๆ - การเปิดสแต็กเปล่าควรโยน NoElementsException หรืออะไรบางอย่างตามแนว ดูคำตอบของ Donal
Konerak

4
ฉันเห็นด้วย. แม้ว่านี่จะมาจากบทช่วยสอนอย่างเป็นทางการ แต่มันก็เป็นตัวอย่างที่ไม่ดี
DJClayworth


7
อาจมีหน่วยความจำรั่วอยู่ตรงนั้น คุณควรตั้งค่า stack [num] = null; เพื่อให้ GC ทำงานได้อย่างถูกต้อง
H.Rabiee

3
ฉันคิดว่าในวิธีการส่วนตัวก็จะถูกต้องในการใช้การยืนยันเนื่องจากมันจะแปลกที่จะมีข้อยกเว้นสำหรับความผิดปกติของชั้นเรียนหรือวิธีการ ในวิธีสาธารณะเรียกมันจากที่อื่นคุณไม่สามารถบอกได้ว่ารหัสอื่นใช้มันอย่างไร มันตรวจสอบ isEmpty () จริง ๆ หรือไม่? คุณไม่รู้
Vlasec

7

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

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

assertคำสั่งมีคำสั่งนี้พร้อมกับตัวเลือกStringข้อความ

ไวยากรณ์สำหรับคำสั่ง assert มีสองรูปแบบ:

assert boolean_expression;
assert boolean_expression: error_message;

ต่อไปนี้เป็นกฎพื้นฐานบางประการที่ควบคุมว่าควรใช้การยืนยันที่ใดและไม่ควรใช้การยืนยันใด ควรใช้การยืนยันสำหรับ:

  1. การตรวจสอบพารามิเตอร์อินพุตของวิธีไพรเวต ไม่ใช่สำหรับวิธีการสาธารณะ publicวิธีการควรโยนข้อยกเว้นปกติเมื่อผ่านพารามิเตอร์ที่ไม่ดี

  2. ที่ใดก็ได้ในโปรแกรมเพื่อรับรองความถูกต้องของข้อเท็จจริงซึ่งเกือบจะเป็นจริงอย่างแน่นอน

ตัวอย่างเช่นหากคุณแน่ใจว่าเป็นเพียง 1 หรือ 2 คุณสามารถใช้การยืนยันดังนี้:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. ตรวจสอบเงื่อนไขการโพสต์ในตอนท้ายของวิธีการใด ๆ ซึ่งหมายความว่าหลังจากดำเนินการตรรกะทางธุรกิจคุณสามารถใช้การยืนยันเพื่อให้แน่ใจว่าสถานะภายในของตัวแปรหรือผลลัพธ์ของคุณสอดคล้องกับสิ่งที่คุณคาดหวัง ตัวอย่างเช่นวิธีการที่เปิดซ็อกเก็ตหรือไฟล์สามารถใช้การยืนยันที่ท้ายเพื่อให้แน่ใจว่าซ็อกเก็ตหรือไฟล์ที่เปิดอยู่แน่นอน

ไม่ควรใช้คำยืนยันใน:

  1. การตรวจสอบพารามิเตอร์อินพุตของวิธีการสาธารณะ เนื่องจากอาจไม่สามารถยืนยันได้เสมอควรใช้กลไกการยกเว้นปกติ

  2. การตรวจสอบข้อ จำกัด เกี่ยวกับสิ่งที่ป้อนโดยผู้ใช้ เช่นเดียวกับข้างต้น

  3. ไม่ควรใช้สำหรับผลข้างเคียง

ตัวอย่างเช่นนี่ไม่ใช่การใช้ที่เหมาะสมเพราะนี่คือการยืนยันที่ใช้สำหรับผลข้างเคียงของการเรียกใช้doSomething()เมธอด

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

กรณีเดียวที่สามารถพิสูจน์ได้คือเมื่อคุณพยายามค้นหาว่ามีการเปิดใช้งานการยืนยันในรหัสของคุณหรือไม่:   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}

5

นอกจากนี้ทุกคำตอบที่ดีให้ที่นี่อย่างเป็นทางการ Java SE 7 คู่มือการเขียนโปรแกรมมีคู่มือการกระชับสวยเกี่ยวกับการใช้assert; มีตัวอย่างหลายจุดที่เป็นแนวคิดที่ดี (และที่สำคัญคือแย่) ในการใช้การยืนยันและแตกต่างจากการโยนข้อยกเว้นอย่างไร

ลิงค์


1
ฉันเห็นด้วย. บทความมีตัวอย่างที่ยอดเยี่ยมมากมาย ฉันชอบวิธีนี้เป็นพิเศษเพื่อให้แน่ใจว่ามีการเรียกใช้เมธอดเมื่อวัตถุถือล็อคเท่านั้น
MiguelMunoz

4

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

หากมีโอกาสที่เงื่อนไขสามารถเกิดขึ้นได้ในชีวิตจริงคุณต้องจัดการกับมัน

ฉันชอบ แต่ไม่ทราบวิธีเปิดใช้งานใน Eclipse / Android / ADT ดูเหมือนว่าจะปิดแม้ในขณะที่การแก้จุดบกพร่อง (มีเธรดเกี่ยวกับสิ่งนี้ แต่มันอ้างถึง 'Java vm' ซึ่งไม่ปรากฏใน ADT Run Configuration)


1
หากต้องการเปิดใช้งานการยืนยันใน eclipse IDE โปรดติดตามtutoringcenter.cs.usfca.edu/resources/…
Ayaz Pasha

ฉันไม่คิดว่าจะมีวิธีการเปิดใช้งานการยืนยันใน Android มันน่าผิดหวังมาก
MiguelMunoz

3

นี่คือคำยืนยันที่ฉันเขียนในเซิร์ฟเวอร์สำหรับโครงการ Hibernate / SQL เอนทิตี bean มีคุณสมบัติบูลีนที่ได้ผลสองอย่างที่เรียกว่า isActive และ isDefault แต่ละคนสามารถมีค่าเป็น "Y" หรือ "N" หรือ null ซึ่งถือว่าเป็น "N" เราต้องการให้แน่ใจว่าไคลเอนต์เบราว์เซอร์ถูก จำกัด เพียงสามค่า ดังนั้นใน setters ของฉันสำหรับคุณสมบัติทั้งสองนี้ฉันได้เพิ่มการยืนยันนี้:

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

แจ้งให้ทราบดังต่อไปนี้

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

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

  3. การยืนยันนี้ไม่มีผลข้างเคียง ฉันสามารถทดสอบค่าของฉันกับชุดสุดท้ายคงที่ซึ่งเปลี่ยนแปลงไม่ได้ แต่ชุดนั้นจะคงอยู่ในการผลิตซึ่งจะไม่มีการใช้

  4. การยืนยันนี้มีอยู่เพื่อตรวจสอบการทำงานที่เหมาะสมของลูกค้า ดังนั้นเมื่อถึงเวลาผลิตเราจะมั่นใจว่าลูกค้าทำงานอย่างถูกต้องเพื่อให้เราสามารถปิดการยืนยันได้อย่างปลอดภัย

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

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

BTW ฉันสามารถเขียนได้เช่นนี้

assert value == null || value.equals("Y") || value.equals("N") : value;

สิ่งนี้ใช้ได้สำหรับค่าสามค่าเท่านั้น แต่ถ้าจำนวนค่าที่เป็นไปได้มากขึ้นรุ่น HashSet จะสะดวกกว่า ฉันเลือกรุ่น HashSet เพื่อให้ประเด็นของฉันมีประสิทธิภาพ


ผมขอสงสัยว่าการใช้ดังกล่าวเล็ก ๆนำประโยชน์จากความเร็วมากกว่าHashSet ArrayListนอกจากนี้การสร้างรายการและชุดครองเวลาค้นหา พวกเขาต้องการได้ดีเมื่อใช้ค่าคงที่ ทั้งหมดบอกว่า +1
maaartinus

จริงทั้งหมด ฉันทำอย่างไม่มีประสิทธิภาพเพื่อแสดงจุดของฉันที่ยืนยันได้ฟรีช้า อันนี้อาจทำให้มีประสิทธิภาพมากขึ้น แต่มีคนอื่นที่ไม่สามารถ ในหนังสือที่ยอดเยี่ยมที่ชื่อว่า "การเขียนรหัสที่มั่นคง" Steve Maguire บอกถึงการยืนยันใน Microsoft Excel เพื่อทดสอบรหัสปรับปรุงส่วนเพิ่มใหม่ที่ข้ามเซลล์ที่ไม่ควรเปลี่ยนแปลง ทุกครั้งที่ผู้ใช้ทำการเปลี่ยนแปลงการยืนยันจะทำการคำนวณสเปรดชีตใหม่ทั้งหมดเพื่อให้แน่ใจว่าผลลัพธ์ที่ตรงกับสิ่งเหล่านั้นด้วยคุณสมบัติการอัพเดทแบบเพิ่มหน่วย มันทำให้เวอร์ชันการดีบักช้าลง แต่พวกเขาตรวจพบข้อผิดพลาดทั้งหมดก่อน
MiguelMunoz

เห็นด้วยอย่างเต็มที่ การยืนยันเป็นประเภทของการทดสอบ - พวกมันมีความหลากหลายน้อยกว่าการทดสอบทั่วไป แต่พวกเขาสามารถครอบคลุมวิธีการส่วนตัวและพวกเขาถูกกว่ามากในการเขียน ฉันจะลองใช้มันให้มากกว่านี้
maaartinus

2

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

การยืนยันทำงานในเวลาทำงาน ตัวอย่างง่ายๆที่สามารถอธิบายแนวคิดทั้งหมดได้อย่างง่ายมากอยู่ในที่นี้ - คำหลักยืนยันทำอะไรใน Java (WikiAnswers)


2

การยืนยันถูกปิดใช้งานตามค่าเริ่มต้น ในการเปิดใช้งานเราต้องเรียกใช้โปรแกรมด้วย-eaตัวเลือก (ความหลากหลายสามารถเปลี่ยนแปลงได้) ตัวอย่างเช่นjava -ea AssertionsDemo.

มีสองรูปแบบสำหรับการใช้การยืนยัน:

  1. ง่าย: เช่น assert 1==2; // This will raise an AssertionError.
  2. ดีกว่า: assert 1==2: "no way.. 1 is not equal to 2"; สิ่งนี้จะยกระดับ AssertionError ด้วยข้อความที่แสดงให้เห็นด้วยและดีกว่า แม้ว่าไวยากรณ์ที่แท้จริงคือassert expr1:expr2ที่ที่ expr2 สามารถเป็นนิพจน์ใด ๆ ที่ส่งคืนค่าฉันได้ใช้มันบ่อยขึ้นเพียงเพื่อพิมพ์ข้อความ

1

ในการสรุป(และนี่เป็นความจริงในหลายภาษาไม่ใช่เฉพาะ Java):

"assert" ส่วนใหญ่จะใช้เป็นตัวช่วยในการดีบักโดยนักพัฒนาซอฟต์แวร์ในระหว่างกระบวนการดีบั๊ก ข้อความยืนยันไม่ควรปรากฏขึ้น หลายภาษามีตัวเลือกเวลารวบรวมที่จะทำให้ละเว้น "asserts" ทั้งหมดเพื่อใช้ในการสร้างรหัส "การผลิต"

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

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

ข้อยกเว้นช่วยให้เงื่อนไขข้อผิดพลาดร้ายแรงสะดวกสบายคือ: "ข้อยกเว้นของกฎ" และสำหรับพวกเขาที่จะได้รับการจัดการโดย code-path ที่เป็น "ข้อยกเว้นของกฎ ... " fly ball!


1

การยืนยันคือการตรวจสอบที่อาจถูกปิด พวกเขาไม่ค่อยได้ใช้ ทำไม?

  • จะต้องไม่ใช้เพื่อตรวจสอบข้อโต้แย้งของเมธอดสาธารณะเนื่องจากคุณไม่สามารถควบคุมได้
  • ไม่ควรใช้การตรวจสอบอย่างง่ายresult != nullเช่นการตรวจสอบดังกล่าวนั้นรวดเร็วมากและแทบไม่มีอะไรจะบันทึก

ดังนั้นมีอะไรเหลือ ตรวจสอบราคาแพงสำหรับเงื่อนไขที่คาดว่าจะเป็นจริง ตัวอย่างที่ดีคือค่าคงที่ของโครงสร้างข้อมูลเช่น RB-tree ที่จริงในConcurrentHashMapของ JDK8 TreeNodesมีไม่กี่ที่มีความหมายดังกล่าวอ้างสำหรับ

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

บางครั้งการตรวจสอบไม่แพงเลย แต่ในเวลาเดียวกันคุณค่อนข้างแน่ใจว่ามันจะผ่าน ในรหัสของฉันมีเช่น

assert Sets.newHashSet(userIds).size() == userIds.size();

ที่ฉันค่อนข้างแน่ใจว่ารายการที่ฉันเพิ่งสร้างมีองค์ประกอบที่ไม่ซ้ำกัน แต่ฉันต้องการเอกสารและตรวจสอบอีกครั้ง


0

โดยพื้นฐานแล้ว "การยืนยันที่แท้จริง" จะผ่านไปและ "การยืนยันที่ผิดพลาด" จะล้มเหลว มาดูกันว่ามันจะทำงานอย่างไร:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}

-8

assertเป็นคำหลัก มันถูกนำมาใช้ใน JDK 1.4 มีสองประเภทของasserts

  1. assertคำสั่งที่ง่ายมาก
  2. assertคำสั่งง่ายๆ

โดยค่าเริ่มต้นassertงบทั้งหมดจะไม่ถูกดำเนินการ หากassertคำสั่งได้รับเท็จมันจะเพิ่มข้อผิดพลาดในการยืนยันโดยอัตโนมัติ


1
มันไม่ได้ให้ตัวอย่างชีวิตจริงใด ๆ ซึ่งเป็นจุดประสงค์ของคำถาม
rubenafo

1
คุณเพิ่งคัดลอกวางจาก: amazon.com/Programmer-Study-1Z0-803-1Z0-804-Certification/dp/
Koray Tugay
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.