כיצד להשתמש בטענות בג'אווה

כתיבת תוכניות שעובדות כהלכה בזמן ריצה יכולה להיות מאתגרת. הסיבה לכך היא שההנחות שלנו לגבי אופן התנהגות הקוד שלנו בעת ביצוע הן לרוב שגויות. שימוש בתכונה של קביעות Java היא דרך אחת לוודא שההיגיון בתכנות שלך תקין.

מדריך זה מציג קביעות Java. תחילה תלמד מהן קביעות וכיצד לציין ולהשתמש בהן בקוד שלך. לאחר מכן, תגלה כיצד להשתמש בטענות לאכיפת תנאים מוקדמים ותנאים שלאחר מכן. לבסוף, תשווה בין קביעות לחריגים, ותגלה מדוע אתה זקוק לשתיהן בקוד שלך.

הורד קבל את הקוד הורד את קוד המקור לדוגמאות במדריך זה. נוצר על ידי ג'ף פרייזן עבור JavaWorld.

מהן קביעות Java?

לפני JDK 1.4, מפתחים השתמשו לעתים קרובות בהערות כדי לתעד הנחות לגבי תקינות התוכנית. הערות אינן חסרות תועלת כמנגנון לבדיקה והנחות באגים. המהדר מתעלם מהערות ולכן אין דרך להשתמש בהן לזיהוי באגים. מפתחים לעתים קרובות גם לא מעדכנים הערות בעת שינוי קוד.  

ב- JDK 1.4 הוצגו קביעות כמנגנון חדש לבדיקה וניקוי הנחות לגבי הקוד שלנו. בעיקרו של דבר, קביעות  הן ישויות הניתנות לביצוע שמבוצעות בזמן ריצה, בהנחה שאפשרתם אותן לבדיקת תוכניות. באפשרותך לתכנת טענות כדי להודיע ​​לך על באגים שבהם מתרחשים הבאגים, ובכך להפחית באופן משמעותי את משך הזמן שאחרת היית מבזבז באגים בתוכנית כושלת.

טענות משמשות לקידוד הדרישות שהופכות תוכנית לתיקון או לא על ידי בדיקת תנאים (ביטויים בוליאניים) לערכים אמיתיים, והודעה למפתח כאשר תנאים כאלה אינם שקריים. שימוש בטענות יכול להגביר מאוד את הביטחון שלך בנכונות הקוד שלך.

כיצד לכתוב קביעה בג'אווה

assertקביעות מיושמות באמצעות ההצהרה java.lang.AssertionErrorוהמעמד. משפט זה מתחיל במילת המפתח assertוממשיך בביטוי בוליאני. זה בא לידי ביטוי בתחביר כדלקמן:

טוענים בוליאן-אקספר ;

אם BooleanExprמעריכים לנכון, שום דבר לא קורה והביצוע ממשיך. אם הביטוי מוערך לשקר, לעומת זאת, AssertionErrorהוא מיידי ונזרק, כפי שהודגם ברשימה 1.

רישום 1:AssertDemo.java (גרסה 1)

מחלקה ציבורית AssertDemo {main public public static (String [] args) {int x = -1; לטעון x> = 0; }}

הקביעה ברישום 1 מצביעה על האמונה של המפתח כי המשתנה xמכיל ערך גדול או שווה ל 0. עם זאת, ברור שזה לא המקרה; assertתוצאות הביצוע של האמירה בתוך נזרק AssertionError.

הידור רישום 1 ( javac AssertDemo.java) והפעל אותו עם קביעות מופעלות ( java -ea AssertDemo). עליכם להתבונן בפלט הבא:

חריג בשרשור "ראשי" java.lang.AssertionError at AssertDemo.main (AssertDemo.java:6)

הודעה זו מעט מוצפנת בכך שהיא אינה מזהה מה גרם AssertionErrorלזריקה. אם אתה רוצה הודעה אינפורמטיבית יותר, השתמש assertבהצהרה הבאה לידי ביטוי:

לטעון בוליאניתמונ : expr ;

הנה, exprכל ביטוי (כולל קריאת שיטה) שיכול להחזיר ערך - אינך יכול להפעיל שיטה עם voidסוג החזרה. ביטוי שימושי הוא מילולי מחרוזת המתאר את הסיבה לכישלון, כפי שהודגם ברשימה 2.

רישום 2:AssertDemo.java (גרסה 2)

מחלקה ציבורית AssertDemo {main public public static (String [] args) {int x = -1; לטעון x> = 0: "x <0"; }}

הידור רישום 2 ( javac AssertDemo.java) והפעל אותו עם קביעות מופעלות ( java -ea AssertDemo). הפעם, עליך להתבונן בפלט המורחב מעט הבא, הכולל את הסיבה לזריקה AssertionError:

חריג בשרשור "ראשי" java.lang.AssertionError: x <0 at AssertDemo.main (AssertDemo.java:6)

בשני הדוגמאות, הפעלה AssertDemoללא האפשרות -ea(אפשר קביעות) אינה גורמת לפלט. כאשר קביעות אינן מופעלות, הן אינן מבוצעות, אם כי הן עדיין קיימות בקובץ המחלקה.

תנאים מוקדמים ותנאי פוסט

קביעות בודקות את הנחות התוכנית על ידי אימות שהתנאים המוקדמים והתנאים האחרונים שלה אינם מופרים, ומזהירים את המפתח כאשר מתרחשת הפרה:

  • תנאי מוקדם הוא תנאי שיש להעריך אל נכון לפני ביצוע רצף כלשהו בקוד. התנאים המוקדמים מבטיחים כי המתקשרים ימשיכו את החוזים שלהם עם אנשי העסקה.
  • Postcondition הוא מצב שיש להעריך אל נכון לאחר ביצוע כמה רצף קוד. התנאים שלאחר מכן מבטיחים כי בעלי עסק מקיימים חוזים עם מתקשרים.

תנאים מוקדמים

אתה יכול לאכוף תנאים מוקדמים על קונסטרוקציות ושיטות ציבוריות על ידי ביצוע בדיקות מפורשות והטלת חריגים במידת הצורך. עבור שיטות עוזרות פרטיות, אתה יכול לאכוף תנאים מוקדמים על ידי ציון קביעות. שקול רישום 3.

רישום 3:AssertDemo.java (גרסה 3)

ייבא java.io.FileInputStream; ייבא java.io.InputStream; ייבא java.io.IOException; class PNG {/ ** * צור מופע PNG, קרא קובץ PNG שצוין ופענח אותו * למבנים מתאימים. * * @param filespec נתיב ושם קובץ PNG לקריאה * * @ זורק NullPointerException מתי filespecהוא *null* / PNG (String filespec) זורק IOException {// אוכף תנאים מוקדמים בבנאים שאינם פרטיים ובשיטות //. אם (filespec == null) זורק NullPointerException חדש ("filespec is null"); נסה (FileInputStream fis = FileInputStream חדש (filespec)) {readHeader (fis); }} פרטי ריק ריק readHeader (InputStream is) זורק IOException {// אשר כי מתקיים תנאי מוקדם בשיטות פרטיות // עוזרות. assert is! = null: "null עבר ל- is"; }} מחלקה ציבורית AssertDemo {ציבורי ריק ריק סטטי (מחרוזת [] טענות) זורק IOException {PNG png = PNG חדש ((args.length == 0)? null: args [0]); }}

The PNG class in Listing 3 is the minimal beginning of a library for reading and decoding PNG (portable network graphics) image files. The constructor explicitly compares filespec with null, throwing NullPointerException when this parameter contains null. The point is to enforce the precondition that filespec not contain null.

It’s not appropriate to specify assert filespec != null; because the precondition mentioned in the constructor’s Javadoc would not (technically) be honored when assertions were disabled. (In fact, it would be honored because FileInputStream() would throw NullPointerException, but you shouldn’t depend on undocumented behavior.)

However, assert is appropriate in the context of the private readHeader() helper method, which will be completed eventually to read and decode a PNG file’s 8-byte header. The precondition that is always be passed a non-null value will always hold.

Postconditions

Postconditions are typically specified via assertions, regardless of whether or not the method (or constructor) is public. Consider Listing 4.

Listing 4:AssertDemo.java (version 4)

public class AssertDemo { public static void main(String[] args) { int[] array = { 20, 91, -6, 16, 0, 7, 51, 42, 3, 1 }; sort(array); for (int element: array) System.out.printf("%d ", element); System.out.println(); } private static boolean isSorted(int[] x) { for (int i = 0; i  x[i + 1]) return false; return true; } private static void sort(int[] x) { int j, a; // For all integer values except the leftmost value ... for (int i = 1; i  0 && x[j - 1] > a) { // Shift left value -- x[j - 1] -- one position to its right -- // x[j]. x[j] = x[j - 1]; // Update insert position to shifted value's original position // (one position to the left). j--; } // Insert a at insert position (which is either the initial insert // position or the final insert position), where a is greater than // or equal to all values to its left. x[j] = a; } assert isSorted(x): "array not sorted"; } }

Listing 4 presents a sort() helper method that uses the insertion sort algorithm to sort an array of integer values. I’ve used assert to check the postcondition of x being sorted before sort() returns to its caller.

The example in Listing 4 demonstrates an important characteristic of assertions, which is that they’re typically expensive to execute. For this reason, assertions are usually disabled in production code. In Listing 4, isSorted() must scan through the entire array, which can be time-consuming in the case of a lengthy array.

Assertions vs. exceptions in Java

Developers use assertions to document logically impossible situations and detect errors in their programming logic. At runtime, an enabled assertion alerts a developer to a logic error. The developer refactors the source code to fix the logic error and then recompiles this code.

Developers use Java’s exception mechanism to respond to non-fatal (e.g., running out of memory) runtime errors, which may be caused by environmental factors, such as a file not existing, or by poorly written code, such as an attempt to divide by 0. An exception handler is often written to gracefully recover from the error so that the program can continue to run.

Assertions are no substitute for exceptions. Unlike exceptions, assertions don’t support error recovery (assertions typically halt program execution immediately — AssertionError isn’t meant to be caught); they are often disabled in production code; and they typically don’t display user-friendly error messages (although this isn’t an issue with assert). It’s important to know when to use exceptions rather than assertions.

When to use exceptions

Suppose you’ve written a sqrt() method that calculates the square root of its argument. In a non-complex number context, it’s impossible to take the square root of a negative number. Therefore, you use an assertion to fail the method if the argument is negative. Consider the following code fragment:

public double sqrt(double x) { assert x >= 0 : "x is negative"; // ... }

It’s inappropriate to use an assertion to validate an argument in this public method. An assertion is intended to detect errors in programming logic and not to safeguard a method from erroneous arguments. Besides, if assertions are disabled, there is no way to deal with the problem of a negative argument. It’s better to throw an exception, as follows:

public double sqrt(double x) { if (x < 0) throw new IllegalArgumentException("x is negative"); // ... }

The developer might choose to have the program handle the illegal argument exception, or simply propagate it out of the program where an error message is displayed by the tool that runs the program. Upon reading the error message, the developer can fix whatever code led to the exception.

אולי שמתם לב להבדל עדין בין הקביעה לבין ההיגיון בזיהוי שגיאות. קביעת הבדיקה x >= 0, ואילו בדיקות ההיגיון לזיהוי שגיאות x < 0. הקביעה אופטימית: אנו מניחים שהטיעון תקין. לעומת זאת, ההיגיון בזיהוי שגיאות הוא פסימי: אנו מניחים שהטיעון אינו תקין. קביעות מתעדות היגיון נכון, ואילו חריגים מתעדים התנהגות שגויה של זמן ריצה.

במדריך זה למדת כיצד להשתמש בטענות כדי לתעד לוגיקה נכונה של התוכנית. למדת גם מדוע טענות אינן תחליף לחריגים, וראית דוגמה שבה שימוש בחריג יהיה יעיל יותר.

סיפור זה, "כיצד להשתמש בטענות ב- Java" פורסם במקור על ידי JavaWorld.