第57項:局所変数の役割ドメインを最小化
本項は、15項目(クラスおよびメンバーのアクセス性を最小化する)と本質的に類似している.ローカル変数の役割ドメインを最小限に抑えることで、コードの可読性とメンテナンス性を向上させ、エラーの可能性を低減できます.
以前のプログラミング言語(C言語など)では、ローカル変数がコードブロックの先頭で宣言されなければならないことが要求されていました.習慣的に、一部のプログラミング猿たちは現在もこのようにしています.これは打ち破るべき習慣だ.Javaでは、文が表示される場所で変数を宣言できるように注意します(Cと同様に、C 99から).
ローカル変数の役割を最小限に抑えるには、最初に使用した場所で宣言するのが最も強力な方法です.変数が使用される前に宣言されると、プログラムの機能を理解しようとする読者にとって、注意力を分散させるだけの要素が増えます.この変数を使用すると、読者は変数のタイプや初期値を覚えていない可能性があります.
ローカル変数を早すぎる宣言すると、その役割ドメインが早すぎるだけでなく、終了が遅すぎる.ローカル変数の役割ドメインは、宣言された点からコードブロックの終了までです.変数が「使用するブロック」の外で宣言されている場合、プログラムがブロックを終了した後も変数は表示されます.変数がターゲット使用領域の前または後に意外に使用されると、結果は災害的になる可能性があります.
ほとんどのローカル変数の宣言には、初期化式が含まれる必要があります.変数を有意義に初期化するのに十分な情報がない場合は、この宣言を延期して、初期化できるまで知らなければなりません.このルールにはtry-cache文に関連する例外があります.変数が式によって初期化されると、この式は、tryブロックの内部で初期化する必要があります(閉じた方法が異常を伝播できない場合を除きます(メソッドにはthrows exceptionが含まれていることを意味します).変数の値がtryブロックの外部で使用される必要がある場合は、tryブロックの前に宣言する必要がありますが、tryブロックの前に「有意義に初期化」することはできません.【原書】283ページの例を参照してください.
ジルコニウムサイクルでは、変数の役割ドメインを最小化するための特別な機会が提供される.従来のforループでもfor-each形式のforループでも、ループ変数(loop variable)を宣言することができ、それらの役割ドメインはちょうど必要な範囲内に限定される.(この領域は、ループボディとforキーワードと本文との間のカッコ内のコードから構成されています.)したがって、ループ終了後にループ変数の内容が不要になった場合、forループはwhileループよりも優先される.
例えば、以下は、集合を巡回するための最初の方法(46)です.
反復器(iterator)にアクセスする必要がある場合は、そのremoveメソッドを呼び出すためかもしれませんが、for-eachループの代わりに従来のforループを使用することが優先されます.
なぜこのforループがwhileループより良いのかを明らかにするには、次のコードフラグメントを考慮してください.2つのwhileループと1つのBUGが含まれています.
2番目のループには、新しい変数i 2が初期化されたが、古い変数iが使用されている「切り取り貼り付け」エラーが含まれています.残念ながら、iはまだ有効な範囲内にあります.結果コードはコンパイルされ、実行時に異常は投げ出されませんが、それがしたことは間違っています.第2のサイクルは、c 2上で反復するのではなく、直ちに終了し、c 2が空の仮想化をもたらす.このプログラムのエラーはひっそりと発生しているので、長い間発見されないかもしれません.
類似の「切り取り貼り付け」エラーがいずれかのサイクル(従来のサイクルでもfor-eachサイクルでも)発生した場合、コードはコンパイルできません.2番目のループが開始される前に、1番目のループの要素(または反復器)変数は、その役割ドメインの範囲外になりました.
さらに、forループを使用すると、通常、2つのループで異なる変数名を使用する必要がないため、このような「切り取り貼り付け」エラーを犯す可能性が大幅に低下する.ループは完全に独立しているため、再利用要素(または反復器)変数の名前に危害はありません.実は、これも流行っています.
使用for循环与使用while循环相比较还有另外一个优势:更简短,从而增强了可读性.
次に、ローカル変数の役割ドメインを最小化する別のループ方法を示します.
この方法について注目すべき事項は、2つの循環変数iおよびnがあり、いずれも完全に正確な役割ドメインを有することである.2番目の変数nは、1番目の変数の限界を格納するために使用され、反復ごとに冗長計算のコストを回避する.通常、ループテストでメソッドの呼び出しが含まれ、反復のたびに同じ結果が返されることを保証する場合は、この習慣を使用します.
最後に局所変数の役割ドメインを最小化する方法は、方法【体が可能な限り】を小さく集中させ、2つの操作(activity)を同じ方法に統合すると、そのうちの1つの操作に関連する局所変数が別の操作を実行するコード範囲内に現れる可能性がある.このような事態を防止するために,この方法を2つに分割すれば,各方法はそれぞれ1つの操作を実行する.
以前のプログラミング言語(C言語など)では、ローカル変数がコードブロックの先頭で宣言されなければならないことが要求されていました.習慣的に、一部のプログラミング猿たちは現在もこのようにしています.これは打ち破るべき習慣だ.Javaでは、文が表示される場所で変数を宣言できるように注意します(Cと同様に、C 99から).
ローカル変数の役割を最小限に抑えるには、最初に使用した場所で宣言するのが最も強力な方法です.変数が使用される前に宣言されると、プログラムの機能を理解しようとする読者にとって、注意力を分散させるだけの要素が増えます.この変数を使用すると、読者は変数のタイプや初期値を覚えていない可能性があります.
ローカル変数を早すぎる宣言すると、その役割ドメインが早すぎるだけでなく、終了が遅すぎる.ローカル変数の役割ドメインは、宣言された点からコードブロックの終了までです.変数が「使用するブロック」の外で宣言されている場合、プログラムがブロックを終了した後も変数は表示されます.変数がターゲット使用領域の前または後に意外に使用されると、結果は災害的になる可能性があります.
ほとんどのローカル変数の宣言には、初期化式が含まれる必要があります.変数を有意義に初期化するのに十分な情報がない場合は、この宣言を延期して、初期化できるまで知らなければなりません.このルールにはtry-cache文に関連する例外があります.変数が式によって初期化されると、この式は、tryブロックの内部で初期化する必要があります(閉じた方法が異常を伝播できない場合を除きます(メソッドにはthrows exceptionが含まれていることを意味します).変数の値がtryブロックの外部で使用される必要がある場合は、tryブロックの前に宣言する必要がありますが、tryブロックの前に「有意義に初期化」することはできません.【原書】283ページの例を参照してください.
ジルコニウムサイクルでは、変数の役割ドメインを最小化するための特別な機会が提供される.従来のforループでもfor-each形式のforループでも、ループ変数(loop variable)を宣言することができ、それらの役割ドメインはちょうど必要な範囲内に限定される.(この領域は、ループボディとforキーワードと本文との間のカッコ内のコードから構成されています.)したがって、ループ終了後にループ変数の内容が不要になった場合、forループはwhileループよりも優先される.
例えば、以下は、集合を巡回するための最初の方法(46)です.
// Preferred idiom for iterating over a collection or array
for (Element e : c) {
... // Do Something with e
}
反復器(iterator)にアクセスする必要がある場合は、そのremoveメソッドを呼び出すためかもしれませんが、for-eachループの代わりに従来のforループを使用することが優先されます.
// Idiom for iterating when you need the iterator
for (Iterator<Element> i = c.iterator(); i.hasNext(); ) {
Element e = i.next();
... // Do something with e and i
}
なぜこのforループがwhileループより良いのかを明らかにするには、次のコードフラグメントを考慮してください.2つのwhileループと1つのBUGが含まれています.
Iterator<Element> i = c.iterator();
while (i.hasNext()) {
doSomething(i.next());
}
...
Iterator<Element> i2 = c2.iterator();
while (i.hasNext()) { // BUG!
doSomethingElse(i2.next());
}
2番目のループには、新しい変数i 2が初期化されたが、古い変数iが使用されている「切り取り貼り付け」エラーが含まれています.残念ながら、iはまだ有効な範囲内にあります.結果コードはコンパイルされ、実行時に異常は投げ出されませんが、それがしたことは間違っています.第2のサイクルは、c 2上で反復するのではなく、直ちに終了し、c 2が空の仮想化をもたらす.このプログラムのエラーはひっそりと発生しているので、長い間発見されないかもしれません.
類似の「切り取り貼り付け」エラーがいずれかのサイクル(従来のサイクルでもfor-eachサイクルでも)発生した場合、コードはコンパイルできません.2番目のループが開始される前に、1番目のループの要素(または反復器)変数は、その役割ドメインの範囲外になりました.
for (Iterator<Element> i = c.iterator(); i.hasNext(); ) {
Element e = i.next();
... // Do something with e and i
}
...
// Compile-time error - cannot find symbol i
for (Iterator<Element> i2 = c2.iterator(); i.hasNext(); ) {
Element e2 = i2.next();
... // Do something with e2 and i2
}
さらに、forループを使用すると、通常、2つのループで異なる変数名を使用する必要がないため、このような「切り取り貼り付け」エラーを犯す可能性が大幅に低下する.ループは完全に独立しているため、再利用要素(または反復器)変数の名前に危害はありません.実は、これも流行っています.
使用for循环与使用while循环相比较还有另外一个优势:更简短,从而增强了可读性.
次に、ローカル変数の役割ドメインを最小化する別のループ方法を示します.
for (int i = 0, n = expensiveComputation(); i < n; i++) {
... // Do something with i;
}
この方法について注目すべき事項は、2つの循環変数iおよびnがあり、いずれも完全に正確な役割ドメインを有することである.2番目の変数nは、1番目の変数の限界を格納するために使用され、反復ごとに冗長計算のコストを回避する.通常、ループテストでメソッドの呼び出しが含まれ、反復のたびに同じ結果が返されることを保証する場合は、この習慣を使用します.
最後に局所変数の役割ドメインを最小化する方法は、方法【体が可能な限り】を小さく集中させ、2つの操作(activity)を同じ方法に統合すると、そのうちの1つの操作に関連する局所変数が別の操作を実行するコード範囲内に現れる可能性がある.このような事態を防止するために,この方法を2つに分割すれば,各方法はそれぞれ1つの操作を実行する.