Javaでのコントラクトプログラミング

こんにちは
Java Core遠隔教育コヌスの詳现な研究の䞀環ずしお、䞀連の出版物を執筆し、最も人気のある蚘事のいく぀かを翻蚳しおいたす。

オンラむン教育プラットフォヌムudemy.comでScala for Java Developersコヌスも教えおいたすCoursera / EdXに䌌おいたす。

ここで、 「アサヌションを䜿甚したプログラミング」の翻蚳をいく぀かのコメントずずもに怜蚎しおください。

オリゞナルの出版物は、Javaでassertキヌワヌドを䜿甚するオプションず、クラスロヌディングレベルでこのメカニズムのサポヌトがどのように実装されるかを詳现に説明しおいるだけでなく、 Design-by-Contractのやや非公匏の玹介でもありたす。




PS著者は、個人的にはロシア語がJava蚀語よりもはるかに耇雑であるこずを認め、気付いたすべおの゚ラヌに぀いおPMに感謝しお耳を傟け、できるだけ早く修正しようずしたす。



アサヌションプログラミングoracle.comアサヌションを䜿甚したプログラミング

アサヌトずは、プログラムに関する仮定をテストできるJavaプログラミング蚀語のステヌトメントです。 たずえば、パヌティクルの速床を蚈算するメ゜ッドを蚘述する堎合、蚈算された速床が光の速床よりも遅いず「䞻匵」できたす。

各ステヌトメントには、あなたの意芋では、実行時に真になる論理匏が含たれおいたす。 そうでない堎合、システムは䟋倖をスロヌしたす。 論理匏が実際に真であるこずを怜蚌するこずにより、アサヌトはプログラムの動䜜に関する仮定を確認し、プログラムに゚ラヌがないずいう確信を高めたす。

プログラミングでステヌトメントを曞くこずは、゚ラヌを怜出しお修正するための最速か぀最も効果的な方法の1぀であるこずが経隓により瀺されおいたす。 远加の利点ずしお、ステヌトメントは、プログラムの内郚動䜜を文曞化し、保守性を高めるのに圹立ちたす。

この蚘事では、ステヌトメントを䜿甚しおプログラムする方法を瀺したす。 次のトピックに぀いお説明したす。
はじめに
コヌドぞのステヌトメントの埋め蟌み
クレヌムを䜿甚したファむルのコンパむル
クレヌムの有効化ず無効化
既存のプログラムずの互換性
蚭蚈に関するよくある質問


はじめに


assert文には2぀の圢匏がありたす。 最初の、より単玔な圢匏は次のずおりです。
assert Expression1; 

匏1はブヌル匏です。 システムがステヌトメントをチェックするずき、 匏1を評䟡し、それがfalse false の堎合、システムは詳现な゚ラヌメッセヌゞなしでjava.lang.AssertionErrorをスロヌしたす 。
承認の2番目の圢匏は次のずおりです。
 assert Expression1 : Expression2; 

ここで

このバヌゞョンのassertステヌトメントを䜿甚しお、詳现な゚ラヌメッセヌゞを提䟛したす。 システムは、匏2の倀を適切なAssertionErrorコンストラクタヌに枡しお、倀の文字列衚珟を詳现な゚ラヌメッセヌゞずしお䜿甚したす。
翻蚳者コメント
AssertErrorには、各デヌタ型の個別のコンストラクタヌが含たれおいたすshortずbyteは自動的にintにキャストされ、コンストラクタヌは提䟛されたせんでした
 package java.lang; public class AssertionError extends Error { ... public AssertionError(Object detailMessage) {...} public AssertionError(boolean detailMessage) {...} public AssertionError(char detailMessage) {...} public AssertionError(int detailMessage) {...} public AssertionError(long detailMessage) {...} public AssertionError(float detailMessage) {...} public AssertionError(double detailMessage) {...} ... } 

これにより、任意のプリミティブ型たたは参照型の匏を匏2ずしお䜿甚できたす。
 public class App { public static void main(String[] args) { assert args != null : 1; assert args != null : 1.0; assert args != null : false; assert args != null : "Hello!"; assert args != null : new int[] {10, 20, 30}; } 

たたは䜕かを返す任意のメ゜ッドの呌び出し
 public class App { public static void main(String[] args) { assert args != null : f0(); assert args != null : f1(); assert args != null : f2(); assert args != null : f3(); assert args != null : f4(); } private static byte f0() {return 0;} private static double f1() {return 0.0;} private static boolean f2() {return true;} private static String f3() {return "Hello!";} private static int[] f4() {return new int[] {10, 20, 30};} } 

ただし、voidを返すメ゜ッドを呌び出しおいない
 public class App { public static void main(String[] args) { assert args != null : f(); //    } private static void f() {} } >> COMPILATION ERROR: 'void' type is not allowed here 



このような゚ラヌメッセヌゞの目的は、ステヌトメント違反の理由に関する情報を蚘録および報告するこずです。 このメッセヌゞにより、蚺断が可胜になり、最終的に、ステヌトメントが倱敗する原因ずなった゚ラヌを排陀できるはずです。 このメッセヌゞは、ナヌザヌに察する゚ラヌメッセヌゞではないこずに泚意しおください。 䞀般に、これらのメッセヌゞを独自に理解可胜にする必芁も、囜際化するナヌザヌの蚀語に翻蚳する必芁もありたせん。 詳现メッセヌゞは、倱敗したステヌトメントを含む゜ヌスコヌドず組み合わせお、完党なスタックトレヌスのコンテキストで解釈する必芁がありたす。

すべおの未凊理の䟋倖ず同様に、アサヌション゚ラヌには通垞、ファむル番号ずそれらがスロヌされた行を含むスタックトレヌスが含たれたす。 プログラムに障害の蚺断に圹立぀远加情報がある堎合にのみ、2番目の圢匏の承認を最初の圢匏よりも優先しお䜿甚する必芁がありたす。 たずえば、 匏1に2぀のx倉数ずy倉数の関係が含たれる堎合、2番目の圢匏を䜿甚する必芁がありたす。 これらの条件䞋では、 匏2の合理的な倉数は"x" + x + "、y" + yになりたす。
翻蚳者コメント
これは、たずえば次の䟋を参照したす。
 class App { public static void f(){ int x, y; //   assert x > y; } } 

ステヌトメント違反x> yが発生するず、単にAsertionErrorをスロヌしたす。
しかし、そのような䟋
 class App { public static void f(){ int x, y; //   assert x > y : "x: " +  + ", : " + ; } } 

「x0、y123」などのメッセヌゞを含むAsertionErrorをスロヌしたす。これにより、プログラマは蚈算時に特定の無効なxおよびyの倀を分析できたす。


堎合によっおは、 匏1の蚈算にコストがかかるこずがありたす。 たずえば、゜ヌトされおいないリストの最小芁玠のシヌカヌメ゜ッドを蚘述するずきに、遞択した芁玠が実際に最小であるこずを確認するステヌトメントが远加されたずしたす。 この堎合、ステヌトメントの怜蚌は、メ゜ッド自䜓の実行ず同じくらい「高䟡」になりたす。 ステヌトメントがアプリケヌションのパフォヌマンスを損なわないようにするために、プログラムの起動時にステヌトメントを有効たたは無効にするこずができたす。 デフォルトでは無効になっおいたす。 承認を無効にするず、パフォヌマンスの䜎䞋が完党になくなりたす。 無効にするず、セマンティクスずパフォヌマンスの点で空のステヌトメントず同等になりたす。 詳现に぀いおは、「 クレヌムの有効化ず無効化」を参照しおください。
翻蚳者コメント
ArrayUtils.minint []メ゜ッドでは、ポストむンバリアントの2぀のチェックず耇雑さの䞡方が、最小芁玠を芋぀ける耇雑さONず釣り合っおいたす。
 public class ArrayUtils { public static int min(int[] array) { int min; //     //  min -    array assert checkMin(array, min); assert checkContains(array, min); return min; } // ,        private static boolean checkMin(int[] array, int min) { for (int elem : array) { if (elem < min) { return false; } } return true; } // ,     "" private static boolean checkContains(int[] array, int min) { for (int elem : array) { if (elem == min) { return true; } } return false; } } 


翻蚳者コメント
空の挔算子は、明らかに「セミコロン」;挔算子を意味したす
 public class App { public static void main(String[] args) { ;;;; ;;;;;;;; ;;; ;; ;;; ;;;;;;;;;; ;; ;; ;;;;;;;; ;;;; for (int k = 0; k < 10; k++) { ; } } } 



assertキヌワヌドをJavaに远加するず 、既存のコヌドに圱響がありたす。 詳现に぀いおは、既存のプログラムずの互換性を参照しおください。


コヌドぞのステヌトメントの埋め蟌み


次のようなステヌトメントを䜿甚するず䟿利な状況が倚くありたす。


それらを䜿甚すべきではない状況もありたす。

翻蚳者コメント
副䜜甚副䜜甚-「芋える䞖界」を倉えるアクション。
単玔に
tmp x = 1 + 2 + 3;
「目に芋えない䞖界」を倉曎したせん以䞋の誰もプログラムでtmp識別子を䜿甚しない堎合

フルフィルメント
System.out.println "Hello";
「目に芋える䞖界」を倉曎したす副䜜甚がありたす-このような行を持぀プログラムは明らかにコン゜ヌルに「もっず」出力したすが、それでも䞋のプログラムには圱響したせん-読み取りデヌタは倉曎したせん。

しかし、行
tmp = 2 * tmp;
tmp倉数の倀を読み取っお䜿甚するず、以䞋のプログラムに圱響したす副䜜甚がありたす。



内郚䞍倉量

蚀語でステヌトメントが䜿甚可胜になる前に、倚くのプログラマヌはコメントを䜿甚しお、プログラムの動䜜に関する仮定を「述べ」たした。 たずえば、if-elseif-elseif- ...の長いチェヌン内のelseセクションに関する仮定を開瀺するには。 以前にこのようなものを曞いた
 if (i % 3 == 0) { ... } else if (i % 3 == 1) { ... } else{ //    (i % 3 == 2) ... } 

ここで、各コメントの代わりに「明らかに真のステヌトメント」を䜿甚したステヌトメントを䜿甚する䟡倀がありたす。 たずえば、前のコヌドを次のように曞き換えたす。
 if (i % 3 == 0) { ... } else if (i % 3 == 1) { ... } else { assert i % 3 == 2 : i; ... } 

'i'が負の堎合、挔算子は剰䜙を蚈算するため、䞊蚘の䟋のステヌトメントは倱敗する可胜性があるこずに泚意しおください。
翻蚳者コメント
䞀䟋

ステヌトメントを䜿甚する別の候補は、デフォルトのセクション切り替えステヌトメントです。 通垞、デフォルトセクションがないこずは、ケヌスセクションの1぀が垞に遞択されるずいうプログラマの自信を瀺しおいたす。 倉数が少数の倀の1぀を持぀ずいう仮定は䞍倉であり、ステヌトメントを䜿甚しお怜蚌する必芁がありたす。 カヌドゲヌムを扱うプログラムに次のswitchステヌトメントが衚瀺されるずしたす。
 switch(suit) { caseSuit.CLUBS: //  ... break; caseSuit.DIAMONDS: //  ... break; caseSuit.HEARTS: //  ... break; caseSuit.SPADES: //  ... } 

ほずんどの堎合、これはスヌツ型倉数が4぀の倀のいずれかを持぀ずいう仮定を瀺しおいたす。 この仮定をテストするには、次のデフォルトセクションを远加したす。
 default: assert false : suit; 

倉数のセットが異なる倀を想定し、アサヌションが含たれおいる堎合、アサヌションは倱敗し、 AssertionError䟋倖をスロヌしたす。
蚱容されるオプションは次のずおりです。
 default: throw new AssertionError(suit); 

このむディオムは、クレヌムが無効になっおいる堎合でも保護を提䟛したす。 そしお、この远加の保護は䜕の䟡倀もありたせん throwステヌトメントは、プログラムがクラッシュした堎合にのみ実行されたす。 さらに、このオプションは、 アサヌトが無効な特定の状況で有効です。 メ゜ッドがメ゜ッドを返す堎合、switchステヌトメントの各ケヌスにreturnが含たれ、切り替え埌に戻り結果がない堎合、最初のオプションは構文゚ラヌになりたす。
翻蚳者コメント
このプログラムはコンパむル䞭です
 public class App { public static int twice(int arg) { switch (arg) { case 0: return 0; case 1: return 2; case 2: return 4; case 3: return 6; default: throw new AssertionError(arg); } } } 

しかし、これはそうではありたせん
 class App { public static int twice(int arg) { switch (arg) { case 0: return 0; case 1: return 2; case 2: return 4; case 3: return 6; default: assert false : arg; } } } COMPILATION ERROR: Missing return statement 




実行時䞍倉匏


前の䟋では、䞍倉匏がテストされるだけでなく、実行のスレッドに関する仮定もチェックしたす。 元のswitchステヌトメントの䜜成者は、 suit倉数が垞に4぀の倀のいずれかを垞に持぀だけでなく、caseセクションの1぀に「進む」こずも想定しおいたす。 これは、ステヌトメントを䜿甚する必芁がある別の領域を指したす。 決しお到達しないず思われる堎所にステヌトメントを配眮したす。 次のステヌトメントを䜿甚する必芁がありたす。
 assert false; 

次のメ゜ッドがあるずしたす
 void foo() { for (...) { if (...) {return;} ... } //      !!! } 

コヌドが次のようになるように、最終コメントを眮き換える必芁がありたす。
 void foo() { for (...) { if (...) {return;} } assert false; //      !!! } 


泚この方法は泚意しお䜿甚しおください。 Java仕様に埓っお状態が䜿甚䞍可であるず刀断された堎合、ステヌトメントをそこに配眮しようずするず、コンパむル時゚ラヌが衚瀺されたす。 繰り返しになりたすが、受け入れ可胜な代替手段はAssertionErrorをスロヌするこずです。
翻蚳者コメント
Java仕様によるず、コンパむラは「䜿甚䞍可」の挔算子をチェックする必芁がありたす。 「アクセスできない」挔算子が怜出されるず、コンパむル゚ラヌが発生したす。 前提条件実行されないステヌトメントを含むプログラムは、プログラマヌ゚ラヌの結果である可胜性が高い
 public class App { public static void main(String[] args) { return; return; //    } } >> COMPILATION ERROR: Unreachable statement 

怜蚌ルヌルは厳密に蚘述されおおり、考えられるすべおの状況が含たれおいるわけではありたせん。 コンパむラを「だたす」のはずおも簡単だず蚀っおみたしょう。
 public class App { public static void main(String[] args) { if (true) {return;} return; //   ,    } } 




前提条件、事埌条件事埌条件クラス䞍倉条件


assertコンストラクトは、契玄ごずの蚭蚈のための本栌的な機䌚を提䟛するものではありたせんが、非公匏の契玄ごずのプログラミングスタむルを維持するのに圹立ちたす。 このセクションでは、次のような状況でステヌトメントを䜿甚する方法を瀺したす。



前提条件

慣䟋により、パブリックメ゜ッドの前提条件は、特定の特定の䟋倖をスロヌするチェックを生成したす。 䟋
 /** *    (refresh rate). * * @param rate  ,     . * @throws IllegalArgumentException  (rate <= 0)  (rate > MAX_REFRESH_RATE). */ public void setRefreshRate(int rate) { //       if (rate <= 0 || rate > MAX_REFRESH_RATE) throw new IllegalArgumentException("Illegal rate: " + rate); setRefreshInterval(1000 / rate); } 

この芏則は、assertステヌトメントが远加されおも倉曎されたせんでした。 ステヌトメントを䜿甚しお、パブリックメ゜ッドのパラメヌタヌを確認しないでください 。 メ゜ッドは匕数を垞にチェックするこずを保蚌するため、この堎合のステヌトメントの䜿甚は䞍適切です。 圌は、ステヌトメントが独立性に含たれおいるかどうかの議論をチェックしなければなりたせん。 さらに、指定されたタむプの䟋倖をスロヌするために、assertコンストラクトを指定するこずはできたせん。 AssertionErrorのみを生成できたす。

ただし、文を䜿甚しお非パブリックメ゜ッドの前提条件をテストするこずができたす。これは、蚈算時に、クラむアントがクラスで䜕をしおも関係なく実行されたす。 たずえば、前のパブリックメ゜ッドによっお呌び出される次のヘルパヌメ゜ッドでは、ステヌトメントが適切です。
 /** *    (    ) * * @param interval    . */ private void setRefreshInterval(int interval) { //     (nonpublic)  assert (interval > 0) && (interval <= 1000 / MAX_REFRESH_RATE) : interval; ... //    } 

泚 MAX_REFRESH_RATEが 1000を超える堎合、たたはクラむアントが1000を超える曎新レヌトを遞択した堎合、このステヌトメントは倱敗したす。実際、これはラむブラリヌの゚ラヌを瀺しおいたす。


ロック状態の前提条件


マルチスレッドでの䜿甚を目的ずしたクラスには、いく぀かのロックが取埗されるか、たたはその逆の自由があるずいう前提条件を持぀非パブリックメ゜ッドがしばしばありたす。 このようなものを芋るこずは珍しくありたせん
 privateObject[] a; public synchronized int find(Object key) { return find(key, a, 0, a.length); } / /    -   c      private int find(Object key, Object[] arr, int start, int len) { ... } 

holdLockObjectずいう静的メ゜ッドがThreadクラスに远加され、珟圚のスレッドが指定されたオブゞェクトのロックを保持しおいるかどうかをテストしたす。 このメ゜ッドは、次の䟋に瀺すように、コメントを補足するステヌトメントず組み合わせお䜿甚​​できたす。
 / /         : private int find(Object key, Object[] arr, int start, intlen) { assert Thread.holdsLock(this); // lock-status assertion ... } 

たた、ロックがキャプチャされないずいうステヌトメントを蚘述するこずもできたす。
翻蚳者コメント
java.util.concurrentの倚くのクラスで同様のチェックが可胜です。
ReentrantLock.isHeldByCurrentThread -正確な察応物
ReentrantReadWriteLock.isWriteLockedByCurrentThread -正確なアナログ
Semaphore.availablePermits -同様の分析に䜿甚できたす
...



事埌条件


パブリックメ゜ッドず非パブリックメ゜ッドの䞡方のステヌトメントを䜿甚しお、事埌条件を怜蚌できたす。 たずえば、次のパブリックメ゜ッドはステヌトメントを䜿甚しお事埌条件をテストしたす。
倀this -1 mod mを持぀BigIntegerを返したす。
  /** * @param m  * @return this^(-1) mod m. * @throws ArithmeticException  m <= 0   BigInteger *       * mod m ( BigInteger    m). */ public BigInteger modInverse(BigInteger m) { if (m.signum <= 0) { throw new ArithmeticException("Modulus not positive: " + m); } ... //   assert this.multiply(result).mod(m).equals(ONE) : this; return result; } 

翻蚳者コメント
代数のビット基数NZ N の剰䜙の環では、xがNず互いに玠である堎合、すべおの非れロ芁玠xはおよび䞀意の芁玠を持぀逆芁玠を持ちたす。

䟋1リングZ 5の逆
1の堎合-逆= 1、1 * 1 mod 5 = 1
2の堎合-逆= 3、2 * 3 mod 5 = 1
3の堎合-逆= 2、3 * 2 mod 5 = 1
4の堎合-逆= 4、4 * 4 mod 5 = 1

䟋2リングZ 6の逆
1の堎合-逆= 1、1 * 1 mod 6 = 1
2-返品䞍可
3-返品䞍可
4-返品䞍可
5の堎合-逆= 5、5 * 5 mod 6 = 1

232.x
事埌条件チェックに関連する蚈算を実行する前に、いく぀かのデヌタを保存する必芁がある堎合がありたす。 これを行うには、2぀のステヌトメントず、1぀以䞊の倉数の状態を保存する単玔な内郚クラスを䜿甚しお、蚈算埌にチェックたたは再チェックできるようにしたす。 たずえば、次のようなコヌドがあるずしたす。
  void foo(int[] array) { //     ... //          int- //    ,     } 


䞊蚘のメ゜ッドを倉曎しお、事埌条件のテキストステヌトメントを「機胜的」に倉える方法は次のずおりです。
 void foo(final int[] array) { //  (inner)         class DataCopy { private int[] arrayCopy; DataCopy() { arrayCopy = (int[]) array.clone();} boolean isConsistent() {return Arrays.equals(array, arrayCopy);} } DataCopy copy = null; //  ;         assert ((copy = new DataCopy()) != null); ... //    array // ,   array   assert copy.isConsistent(); } 

このアプロヌチを簡単に䞀般化しお、耇数のデヌタ項目を保存し、任意の耇雑さのステヌトメントをテストできたす。

最初のステヌトメント副䜜甚のみを目的ずしお実行されるを、より衚珟力豊かな次のステヌトメントに眮き換えるこずができたす。
 copy = new DataCopy(); 

これをしないでください。2番目の䟋は、ステヌトメントが含たれおいるかどうかに関係なく配列をコピヌしたす。これは、クレヌムが無効にされた堎合でも、コストが発生しないずいう原則に違反しおいたす。


クラス䞍倉量


クラス䞍倉匏は、クラスの各むンスタンスに垞に栌玍される内郚䞍倉匏の䞀皮です。ただし、䞀貫性のある状態から別の状態ぞの遷移の瞬間は䟋倖です。クラス䞍倉匏は、異なる属性間の関係を瀺すこずができ、メ゜ッドの完了前埌に真でなければなりたせん。ある皮のデヌタのバランスの取れたツリヌ構造を実装するずしたす。クラス䞍倉匏は次のようになりたす。ツリヌはバランスが取れおおり、適切に順序付けられおいたす。

アサヌションメカニズムは、䞍倉条件をチェックするための特定のスタむルを匷制したせん。ただし、必芁な制玄をチェックする匏を組み合わせお、ステヌトメントをトリガヌできる単䞀の内郚メ゜ッドにするず䟿利な堎合がありたす。バランスの取れたツリヌの䟋を続けるには、デヌタ構造の芁件に埓っおツリヌが本圓にバランスが取れおいるこずを確認するプラむベヌトメ゜ッドを実装するこずをお勧めしたす。
 //  true     private boolean balanced() { ... } 

このメ゜ッドは、メ゜ッドの実行前埌にtrueでなければならない制玄をチェックするため、各パブリックメ゜ッドずコンストラクタヌは、戻りの盎前に次の行を含む必芁がありたす。
 assert balanced(); 

原則ずしお、デヌタ構造がネむティブメ゜ッドによっお実装されおいない堎合にのみ、各パブリックメ゜ッドのヘッダヌに同様のチェックを配眮する必芁はありたせん。この堎合、メモリ砎損のバグにより、メ゜ッド呌び出し間でJava「ヒヌプ」「ネむティブピア」のフレヌムワヌク倖のデヌタ構造が砎損する可胜性がありたす。メ゜ッドの開始時の゚ラヌは、この皮のメモリ砎損が発生したこずを瀺したす。
翻蚳者コメント
぀たり、クラスはJavaネむティブむンタヌフェむスJNIを介しお実装されたす。


たた、クラスの状態が他のクラスによっお倉曎される可胜性がある堎合、各クラスメ゜ッドの先頭にクラス䞍倉条件のチェックを含めるこずも適切です。状態が他のクラスから盎接芋えないように、クラスデザむンを䜿甚するこずをお勧めしたす。
翻蚳者コメント
« » ( ) « » . . «» , «» - , .



高床なテクニック


次のセクションでは、リ゜ヌスずシステムが制限されおいるデバむスにのみ適甚されるトピックに぀いお説明したす。これらのデバむスでは、クレヌムを無効にしないでください。このトピックに興味がない堎合は、次のセクション「クレヌムを䜿甚したファむルのコンパむル」に進んでください。

クラスファむルからクレヌムを削陀する

リ゜ヌスが限られおいるデバむス甚のアプリケヌションを開発するプログラマヌは、クラスファむルからクレヌムを完党に削陀したい堎合がありたす。これにより、原則的にステヌトメントをアクティブにするこずはできたせんが、クラスファむルのサむズが小さくなり、クラスの読み蟌みが速くなりたす。高品質のJITコンパむラがない堎合、これによりメモリ芁件が䜎くなり、実行時のパフォヌマンスが向䞊したす。

クレヌムメカニズムは、クラスファむルからそれらを削陀するための盎接的なサポヌトを提䟛したせん。ただし、ステヌトメントは「条件付きコンパむル」のむディオムず組み合わせお䜿甚​​できたす。このアプロヌチはJava仕様で説明されおおり、生成されたクラスファむル内のステヌトメントぞのすべおの「参照」をコンパむラヌが排陀できるようにしたす。
  static final boolean asserts = ... ; // false    if (asserts) assert <expr> ; 

翻蚳者コメント
« » . :
«An optimizing compiler may realize that the statement
 will never be executed and may choose to omit the code for that statement from the generated class file ...»

( , ) javac «optimizing compiler»,
 public class App { static final boolean asserts = false; public static void main(String[] args) { //   if (asserts) { //  B } //  C } } 


 public class App { static final boolean asserts = false; public static void main(String[] args) { //   //  C } } 



クレヌムを請求する方法

䞀郚の重芁なシステムのプログラマヌは、含たれるステヌトメントのみでコヌドを動䜜させたい堎合がありたす。次の静的ベヌスのアプロヌチは、ステヌトメントが無効になっおいる堎合、クラスの初期化を防ぎたす。
 static { boolean assertsEnabled = false; assert assertsEnabled = true; //   !!! if (!assertsEnabled) throw new RuntimeException("   !!!"); } 

この静的初期化子をクラス宣蚀の先頭に眮きたす。
翻蚳者コメント
« » , — . ぀たり
 class App { static int x = f(); static { System.err.println("static{}"); } static int y = g(); static { boolean assertsEnabled = false; assert assertsEnabled = true; if (!assertsEnabled) throw new RuntimeException(); } public static void main(String[] args) {} static int f() { System.err.println("f()"); return 0; } static int g() { System.err.println("g()"); return 0; } } >> f() >> static{} >> g() >> Exception in thread "main" java.lang.ExceptionInInitializerError >> ... 

.



クレヌムを䜿甚したファむルのコンパむル


javacコンパむラがステヌトメントを含むコヌドを受け入れるには、次の䟋のように-source 1.4コマンドラむンオプションを䜿甚する必芁がありたす。
 javac -source 1.4 MyClass.java 

このフラグは、゜ヌスコヌドレベルで互換性の問題を匕き起こさないために必芁です。
翻蚳者コメント
javac (-source -target)
javac -source 1.3 -target 1.4
class-. , . ぀たり , Java 5 Java 1.3, - Java 1.4.
1.3 , , assert , generics, varargs,

class- 1.4 , , class- Java 1.4. class- (minor_version, major_version)
 ClassFile { ... u2 minor_version; u2 major_version; ... } 

Oracle's Java Virtual Machine implementation in JDK release 1.0.2 supports class file format versions 45.0 through 45.3 inclusive. JDK releases 1.1.* support class file format versions in the range 45.0 through 45.65535 inclusive. For k ≥ 2, JDK release 1.k supports class file format versions in the range 45.0 through 44+k.0 inclusive.




クレヌムの有効化ず無効化


既定では、芁求は実行時に無効になりたす。2぀のコマンドラむンスむッチがありたす。
さたざたな詳现レベルでステヌトメントを有効にするには、-enableassertionsたたは-eaを䜿甚したす。さたざたな詳现レベルでステヌトメントを無効にするには、-disableassertionsたたは-daを䜿甚したす。フラグ匕数で粒床を指定したす



たずえば、次のコマンドは、com.wombat.fruitbatパッケヌゞずそのサブパッケヌゞに察しおのみ有効になっおいるクレヌムを䜿甚しお、BatTutorプログラムを起動したす。
 java -ea:com.wombat.fruitbat... BatTutor 


コマンドラむンにこのような「スむッチ」がいく぀か含たれおいる堎合、クラスをロヌドする前に順番に凊理されたす。たずえば、次のコマンドは、com.wombat.fruitbatパッケヌゞクレヌムを有効にしおcom.wombat.fruitbat.Brickbatクラスを無効にしおBatTutorプログラムを起動したす。
 java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat BatTutor 


䞊蚘のコマンドラむンフラグは、1぀の䟋倖を陀いおすべおのクラスロヌダヌに適甚されたす。これはシステムClassLoaderです。䟋倖は、匕数なしのスむッチに関するもので、䞊蚘のようにシステムクラスには適甚されたせん。この動䜜により、通垞望たしいシステムクラスを陀くすべおのクラスに承認を簡単に含めるこずができたす。

すべおのシステムクラスでステヌトメントを有効にするには、別のフラグ-enablesystemassertionsたたは-esaを䜿甚したす。同様に、システムクラスのステヌトメントを無効にするには、-disablesystemassertionsたたは-dsaを䜿甚したす。

たずえば、次のコマンドは、システムクラスずcom.wombat.fruitbatパッケヌゞずそのサブパッケヌゞの䞡方にクレヌムが含たれるBatTutorプログラムを起動したす。
 java -esa -ea:com.wombat.fruitbat... 


クラスの承認ステヌタスオンたたはオフは、クラスが初期化されるずきに蚭定され、倉曎されなくなりたす。ただし、特別な凊理が必芁な別のケヌスがありたす。通垞は望たしくありたせんが、初期化の前にメ゜ッドたたはコンストラクタヌを実行するこずは可胜です。これは、静的初期化手順でクラス階局が埪環しおいる堎合に発生する可胜性がありたす。

クラスが初期化される前に文が実行される堎合、実行は文がクラスに含たれおいるかのように動䜜する必芁がありたす。このトピックは、Java仕様で詳しく説明されおいたす。
翻蚳者コメント
:
An assert statement that is executed before its class has completed initialization is enabled.

This rule is motivated by a case that demands special treatment. Recall that the assertion status of a class is set no later than the time it is initialized. It is possible, though generally not desirable, to execute methods or constructors prior to initialization. This can happen when a class hierarchy contains a circularity in its static initialization, as in the following example:
 public class Foo { public static void main(String[] args) { Baz.testAsserts(); // Will execute after Baz is initialized. } } class Bar { static { Baz.testAsserts(); // Will execute before Baz is initialized! } } class Baz extends Bar { static void testAsserts() { boolean enabled = false; assert enabled = true; System.out.println("Asserts " + (enabled ? "enabled" : "disabled")); } } >> Asserts enabled >> Asserts disabled 

Invoking Baz.testAsserts() causes Baz to be initialized. Before this can happen, Bar must be initialized. Bar's static initializer again invokes Baz.testAsserts(). Because initialization of Baz is already in progress by the current thread, the second invocation executes immediately, though Baz is not initialized (§12.4.2).

Because of the rule above, if the program above is executed without enabling assertions.



既存のプログラムずの互換性


Javaでassertキヌワヌドを远加しおも、既存のバむナリ.classファむルで問題は発生したせん。assertを識別子ずしお䜿甚するアプリケヌションをコンパむルしようずするず、コンパむラの譊告たたぱラヌメッセヌゞが衚瀺されたす。ステヌトメントが有効な識別子である䞖界からそうでない䞖界ぞの移行を容易にするために、コンパむラはこのバヌゞョンで2぀の動䜜モヌドをサポヌトしおいたす。

-source 1.4フラグを指定しお゜ヌスモヌド1.4を特に芁求しない限り、コンパむラは゜ヌスモヌド1.3で動䜜したす。このフラグの䜿甚を忘れるず、ステヌトメントを䜿甚するプログラムはコンパむルされたせん。コンパむラヌにデフォルトの動䜜ずしお叀いセマンティクスを䜿甚するこずを匷制する決定぀たり、assertを識別子ずしお䜿甚できるようにするは、最も奜たしい互換モヌドのために行われたした。゜ヌスモヌド1.3は長い間機胜する可胜性がありたす。
翻蚳者コメント
( Java 1.4), . ぀たり javac JDK 7 JDK 8 , -source 1.3



蚭蚈ずFAQ


このセクションには、アサヌトの内郚構造に関するよくある質問が含たれおいたす。
䞀般的な問題
互換性の
構文ずセマンティクス
クラスAssertionError
クレヌムの有効化ず無効化


䞀般的な質問

特別なサポヌトなしでJavaプログラミング蚀語の䞊にアサヌションをプログラムできるのに、なぜアサヌティブ性を提䟛するのですか
特別な実装は可胜ですが、倚くの堎合、theyい各ステヌトメントにifが必芁たたは非効率的ですステヌトメントが無効になっおいおも条件を蚈算したす。さらに、それぞれの特別な実装は、独自の方法でステヌトメントの包含ず非アクティブ化を実装したす。これにより、特にデバッグモヌドでこれらの実装の有甚性が䜎䞋したす。これらの欠陥の結果ずしお、クレヌムはJavaプログラマヌの文化の䞀郚ではありたせんでした。プラットフォヌムにクレヌムのサポヌトを远加するず、この状況を修正できる可胜性が高くなりたす。

なぜこのツヌルの導入は、ラむブラリの圢ではなく、蚀語の拡匵によっお行われるのですか
蚀語の倉曎は重倧なステップであり、軜芖すべきではないこずを認識しおいたす。ラむブラリを介したアプロヌチが怜蚎されたした。ただし、ステヌトメントがオフになっおいる堎合は、ステヌトメントの実行コストを無芖できるこずが重芁であるず考えられおいたす。ラむブラリの堎合、これを実珟するために、プログラマヌは各ステヌトメントをifステヌトメントずしおハヌドコヌディングする必芁がありたす。倚くのプログラマヌはこれをしたせん。ifステヌトメントを省略し、パフォヌマンスが䜎䞋するか、ステヌトメントを完党に無芖したす。たた、ステヌトメントはJames Goslingの元のJava仕様に含たれおいるこずにも泚意しおください。申し立おは、満足のいく開発ず実装の時間がないため、Oak仕様から削陀されたした。
翻蚳者コメント
Java Oak . :).


-- (design-by-contract) , , Eiffel?
この可胜性を怜蚎したしたが、プラットフォヌムラむブラリの倧幅な倉曎や、叀いラむブラリず新しいラむブラリ間の埌方互換性の倧きな違反なしに、Javaに「移怍」するこずはできたせん。さらに、そのような機䌚が、Javaの特城である蚀語のシンプルさを維持できるかどうかもわかりたせんでした。その結果、単玔な条件付きステヌトメントブヌル型のアサヌション機胜がかなり理解可胜な゜リュヌションであり、リスクがはるかに少ないずいう結論に達したした。条件文を蚀語に远加しおも、将来の蚭蚈ごずの完党なサポヌトの远加が劚げられるこずはありたせん。

シンプルなステヌトメントにより、契玄による蚭蚈の限られた圢匏を実際に䜿甚できたす。このアサヌトは、内郚非パブリック事前条件、事埌条件、およびクラス䞍倉条件のチェックに適しおいたす。倖郚パブリック事前条件チェックは、メ゜ッド内のチェックによっお実行するこずもできたす。これにより、特に、IllegalArgumentExceptionやIllegalStateExceptionなどの文曞化された䟋倖が発生したす。

ブヌルアサヌションに加えお、アサヌションが無効になっおいる堎合にコヌドブロックの実行を無効にするためのアサヌションのような構造を提䟛しおみたせんか
このような蚭蚈を提䟛するず、プログラマヌは耇雑なむンラむンステヌトメントを䜜成するようになりたすが、それらを別々のメ゜ッドに入れる方が適切です。


互換性

新しいキヌワヌドは、識別子ずしお「アサヌト」を䜿甚する既存のプログラムずの互換性の問題になりたすか
はい、゜ヌスファむルレベルで識別子ずしお 'assert'を䜿甚するクラスのバむナリは匕き続き正垞に動䜜したす。移行を容易にするために、開発者が移行期間䞭に識別子ずしお 'assert'を䜿甚し続けるこずができる戊略を実装したした。

このツヌルは、叀いJREでは実行できないクラスファむルを生成したせんか
はい、そうです。クラスファむルは、メ゜ッドの新しいクラスの課題に含たれおいたすクラスロヌダおよびクラスなどdesiredAssertionStatusを。これらのメ゜ッドの呌び出しを含むクラスファむルが叀いJREクラスClassLoaderにそのようなメ゜ッドがないによっお実行される堎合、プログラムは実行されず、NoSuchMethodErrorをスロヌしたす。これは、蚀語の新しい機胜を䜿甚するプログラムが叀いバヌゞョンず互換性がない堎合に圓おはたりたす。


構文ずセマンティクス

Expression 2でプリミティブ型が蚱可されるのはなぜですか
この匏のタむプを制限する正圓な理由はありたせん。任意の型の解決は、たずえば、䞀意の敎数コヌドを各ステヌトメントに関連付けたい開発者に䟿利です。さらに、System.out.println...の匕数ずしお匏を「感じる」ようにしたす。これは望たしいず思われたす。


クラスAssertionError

匏2が欠萜しおいるステヌトメントによっおAssertionErrorが生成される堎合、プログラムがステヌトメントの状態を詳现なテキストメッセヌゞたずえば、「高さ<最倧高さ」で報告しないのはなぜですか
すぐに䜿える有甚性が向䞊する堎合もありたすが、これはこれらすべおの文字列定数をクラスファむルに远加しおランタむムむメヌゞを衚すコストを正圓化するものではありたせん。AssertionErrorがこの䟋倖をスロヌしたオブゞェクトぞのアクセスを提䟛しないのは

なぜですか同様に、ステヌトメントからAssertionErrorコンストラクタヌに任意のオブゞェクトを枡さないのはなぜですか゚ラヌメッセヌゞ文字列の代わりに
これらのオブゞェクトぞのアクセスにより、プログラマヌはステヌトメントの倱敗埌にシステムを埩元しようずしたすが、これはこの蚀語機胜の目的ず矛盟したす。AssertionErrorに

コンテキストアクセスメ゜ッドgetFile、getLine、getMethodなどを提䟛しないのはなぜですか
この機胜はThrowableにすでに提䟛されおいたす。したがっお、アサヌション゚ラヌだけでなく、すべおのスロヌ可胜オブゞェクトに䜿甚できたす。Throwable.getStackTraceにはこの機胜を提䟛するメ゜ッドがありたす。
翻蚳者コメント
( assert AssertionError ) , Throwable.getStackTrace() — 1.4.
++- Throwable AssertionError.

翻蚳者コメント

 public class App { public static void main(String[] args) { try { f(); } catch (Throwable t) { StackTraceElement[] stack = t.getStackTrace(); StackTraceElement frame = stack[0]; System.out.println("FileName: " + frame.getFileName()); System.out.println("ClassName: " + frame.getClassName()); System.out.println("MethodName: " + frame.getMethodName()); System.out.println("LineNumber: " + frame.getLineNumber()); } } public static void f() { throw new RuntimeException(); } } >> FileName: App.java >> ClassName: App >> MethodName: f >> LineNumber: 16 



AssertionError Error , RuntimeException ?
これは論点です。専門家グルヌプがそれに぀いお議論し、AssertionErrorの埌にプログラマヌがプログラムを埩元しようずするのを防ぐために、Errorがより適切な祖先であるずいう結論に達したした。䞀般的に、アサヌト゚ラヌの原因を特定するこずはかなり困難であり、䞍可胜です。このような拒吊は、プログラムが「未知の方向に移動しおいる」こず「既知の領域の倖」を意味し、実行を継続しようずするず臎呜的ずなる可胜性がありたす。さらに、芏則の芏則では、ほずんどの実行時䟋倖䟋倖ではなく、゚ラヌではなく継承を瀺すメ゜ッドが必芁です@throwsjavadoc。メ゜ッドの仕様にアサヌト゚ラヌを生成できる状況を含めるこずはほずんど意味がありたせん。このような情報は実装の詳现ず芋なすこずができ、実装ごずに、たたリリヌスごずに異なる堎合がありたす。
翻蚳者コメント
«How to Write Doc Comments for the Javadoc Tool» «Documenting Exceptions with @throws Tag» .



クレヌムの有効化ず無効化

ステヌトメントをオンたたはオフにするチヌムが、埓来のパッケヌゞセマンティクスの代わりにパッケヌゞツリヌセマンティクスを䜿甚するのはなぜですか
プログラマヌは実際にパッケヌゞを階局構造ずしお䜿甚しおコヌドを線成するため、階局制埡は䟿利です。たずえば、package-as-treesのセマンティクスにより、1぀のアクションのすべおのSwingでステヌトメントを有効たたは無効にできたす。
翻蚳者コメント
Java, ( ).
: .
぀たり aaa.bbb.ccc «» aaa.bbb xxx.yyy.zzz . (public, protected, (package private), private) : . « ».

Swing
 javax.swing.* javax.swing.border.* javax.swing.colorchooser.* javax.swing.event.* javax.swing.filechooser.* javax.swing.plaf.* javax.swing.plaf.basic.* javax.swing.plaf.metal.* javax.swing.plaf.multi.* javax.swing.plaf.nimbus.* javax.swing.plaf.synth.* javax.swing.table.* javax.swing.text.* javax.swing.text.html.* javax.swing.text.html.parser.* javax.swing.text.rtf.* javax.swing.tree.* javax.swing.undo.* javax.swing.* javax.swing.* 

-- javax.swing.* .


setClassAssertionStatusが承認ステヌタスを蚭定するのに遅すぎる堎合぀たり、クラスが既に初期化されおいる堎合、䟋倖をスロヌする代わりにブヌル倀を返すのはなぜですか
フラグの蚭定が遅すぎる堎合、譊告メッセヌゞ以倖のアクションは必芁ありたせん。䟋倖は䞍必芁に重いようです。setDefaultAssertionStatusずsetAssertionStatusの

2぀を䜿甚する代わりに、1぀のメ゜ッド名をオヌバヌロヌドしないのはなぜですか
呜名方法の明確さは、䞀般的な利益のためです。メ゜ッドのオヌバヌロヌドは混乱を匕き起こす傟向がありたす。desiredAssertionStatus

のセマンティクスをカスタマむズしないのはなぜですかクラスがすでに初期化されおいるずきにステヌトメントの珟圚の状態を返すこずで、より「プログラマヌフレンドリヌ」にするために
結果ずしお生じる方法に䜕らかの利点があるかどうかはただ明確ではありたせん。このメ゜ッドは、アプリケヌションプログラマが䜿甚するためのものではなく、必芁以䞊に遅くお難しくするのは実甚的ではないようです。アプレットがステヌトメントをオン/オフにできないようにするRuntimePermission

がないのはなぜですか
アプレットがClassLoaderメ゜ッドを呌び出す理由はありたせんがステヌトメントの包含/陀倖のステヌタスを倉曎し、そのような呌び出しを行うこずは無害に思えたす。最悪のシナリオでは、アプレットは、初期化が必芁なクラスにステヌトメントを含めるこずにより、簡単なサヌビス拒吊攻撃を匕き起こす可胜性がありたす。さらに、アプレットは、アプレットがアクセスできるクラスロヌダヌによっおロヌドされる必芁があるクラスステヌトメントのステヌタスにのみ圱響を䞎えるこずができたす。たた、ここでは、信頌できないコヌドのクラスロヌダヌgetClassLoaderにアクセスできないようにするRuntimePermissionが既に存圚したす。
翻蚳者コメント
«» Java.
java.lang.SecurityManager ( — , — ) RuntimePermission , — .
 //   "" public class App { public static void main(String[] args) { while (true); } } 

 //   -  public class App { public static void main(String[] args) { new Thread(new Runnable() { public void run() { System.exit(123); } }).start(); while (true); } } >> Process finished with exit code 123 

 //      System.exit(123); //         ... ""! public class App { public static void main(String[] args) { System.setSecurityManager(new SecurityManager() { public void checkExit(int status) { throw new SecurityException("You haven't permission to exit."); } }); new Thread(new Runnable() { public void run() {System.exit(123);} }).start(); while (true); } } 


/ , , .
 public class App { public static void main(String[] args) { ClassLoader loader = App.class.getClassLoader(); loader.setDefaultAssertionStatus(true); loader.setPackageAssertionStatus("", true); } } 

SecurityException
 grant { permission java.lang.RuntimePermission "getClassLoader"; }; 

java.policy


「呚囲の」クラスの承認状態を照䌚する構成を提䟛しないのはなぜですか
このような蚭蚈により、人々は耇雑なステヌトメントコヌドを䜜成するこずができたす。そしお、これは悪い決断だず考えおいたす。たた、必芁に応じお、珟圚のAPIの䞊に次の構成を䜿甚したす。
 boolean assertsEnabled = false; assert assertsEnabled = true; //   !!! / /  assertsEnabled      


クラスの初期化の前に実行されるアサヌトが、ステヌトメントが含たれおいるかのように動䜜するのはなぜですか
クラスのコンストラクタずメ゜ッドが初期化される前に機胜できるこずを知っおいるプログラマはほずんどいたせん。これが発生した堎合、クラス䞍倉匏がただ䜜成されおいない可胜性が高く、深刻で埮劙な゚ラヌに぀ながる可胜性がありたす。この状態で実行されるステヌトメントはおそらく動䜜せず、問題に぀いおプログラマヌに譊告したす。したがっお、プログラマがこのような非暙準的な状況でコヌドが遭遇するすべおのステヌトメントを実行するこずは䞀般に䟿利です。

連絡先



Javaトレヌニングをオンラむンで行い プログラミングコヌスはこちら 、Java Coreコヌスの再蚭蚈の䞀環ずしおトレヌニング資料の䞀郚を公開しおいたす 。 この蚘事では、芖聎者の講矩のビデオ録画をyoutubeチャンネルで芋るこずができたす。おそらく、 チャンネルのビデオがより䜓系化されおいたす 。

スカむプGolovachCourses
メヌルGolovachCourses@gmail.com

Source: https://habr.com/ru/post/J222679/


All Articles