effective java 輪読会 項目45-48

24
Effective Java 輪読会 Item 45-48 開発部 陳映融 2014/2/19

Upload: appresso-engineering-team

Post on 11-Jul-2015

369 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: Effective Java 輪読会 項目45-48

Effective Java 輪読会Item 45-48

開発部 陳映融 2014/2/19

Page 2: Effective Java 輪読会 項目45-48

第8章 プログラミング一般 項目45 ローカル変数のスコープを最小限にする

項目46 従来の for ループより for-each ループを選ぶ

項目47 ライブラリーを知り、ライブラリーを使う

項目48 正確な答えが必要ならば、float と double を避ける

項目49 ボクシングされた基本データより基本データ型を選ぶ

項目50 他の型が適切な場所では、文字列を避ける

項目51 文字列結合のパフォーマンスに用心する

項目52 インタフェースでオブジェクトを参照する

項目53 リフレクションよりインタフェースを選ぶ

項目54 ネイティブメソッドを注意して使用する

項目55 注意して最適化する

項目56 一般的に受け入れられている命名規約を守る

2

Page 3: Effective Java 輪読会 項目45-48

Item 45ローカル変数のスコープを最小限にする

Page 4: Effective Java 輪読会 項目45-48

ローカル変数のスコープ

4

復習:ローカル変数のスコープは宣言のあと、ブロックが終わるまで

ローカル変数のスコープを最小化することで、コードの可読性と保守性が向上し、誤りの可能性が減る

本質的には項目13 「クラスとメンバーへのアクセスを最小限にする」と類似

void scopeSample() {... // 未宣言int var = 0; // ここからが var のスコープ{

...var--; // 内部ブロックもスコープに含まれる

}var--; // 所在ブロックが終わるまで有効

}

Page 5: Effective Java 輪読会 項目45-48

ローカル変数は初めて使うときに宣言

5

ローカル変数を早めに宣言する場合

処理を理解しようとする読み手の注意を逸らしてしまう

たくさんの変数を(一時的に)覚えることで、大事な部分への注意が散らしてしまう

変数使用された時に型と初期値を思え出せないかも

// ブロックの先頭に宣言するのは C 言語のスタイル、やらないことvoid declarationInCStyle() {

int i = 0, j = 0, k = 0; // ループ制御用String s1, s2, ... // 文字列変数いろいろdouble d1, d2, ... // 数値変数いろいろ... // 変数宣言たくさん...覚えきれんわ!{

...x = y; // えっと、こいつらの初期値と型は...何だっけ?

}}

Page 6: Effective Java 輪読会 項目45-48

ローカル変数宣言と初期化子

6

ほとんどローカル変数宣言は初期化子を含んでいるべき

初期化子を含むことで初期化漏れ防止

変数を合理的に初期化する情報を揃えてから変数を宣言(&初期化)

初期化に必要な情報が足りない場合、宣言を先送りすべき

void declarationWithoutInit() {int var; // まだ使わないけど先に宣言しておこう... // いろいろ書いたら数ページになって(そこまで書いたらメソッドを分けたほうがいいけ

ど)var++; // えっと、初期化した...よね?...

}

void declarationWithInitInfo() {Variable tooEarly; // 初期化の情報が足りていない ⇒ 早すぎる... // いろいろ情報収集Info info = collectInfo(args); // ここに来てやっと情報が揃ったVariable properTime = new Variable(info); // 使用直前に宣言&初期化 ⇒ 適切なタイミングuseVariable(properTime);...

}

Page 7: Effective Java 輪読会 項目45-48

ローカル変数宣言と初期化子

7

前述規則(ローカル変数宣言は初期化子を含んでいるべき)の例外: try-catch 文

チェックされる例外を投げるメソッドの戻り値で変数を初期化する場合

Java 1.7 の AutoCloseable の導入で Closeable も規則適用可能に

try-with-resources:

static void tryCatchSample() {BufferedReader br = null;try {

br = new BufferedReader(new FileReader(“filePath”)); // 初期化で例外を投げるかもSystem.out.println(br.readLine()); // ここにも例外を投げる可能性がある

} catch (IOException ex) {...

} finally {if (br != null) br.close(); // try ブロックの外での使用、宣言はブロック外でなければ

}}

static void tryWithResourcesSample() throws IOException {try (BufferedReader br =

new BufferedReader(new FileReader(“filePath”))) { // br のスコープは try ブロック内System.out.println(br.readLine());

}}

Page 8: Effective Java 輪読会 項目45-48

ループでの変数スコープ最小化

8

for と for-each はループ変数を宣言できる

変数スコープはループ本体と for 予約語の後の括弧内のコードに限定

ループごとの冗長な計算を同じ結果が返される場合、ループ変数で回避

// コレクションをイテレートする好ましいイデオムfor (Element e : c) {

doSomething(e);}

// リリース 1.5 より前のジェネリックがない場合、いまも使えるけど...for (Iterator i = c.iterator(); i.hasNext(); ) {

doSomething((Element) i.next()); }

// 初期化時に一回だけ処理for (int i = 0, n = expensiveComputation(); i < n; i++) {

doSomething(i); }

Page 9: Effective Java 輪読会 項目45-48

ループでの変数スコープ最小化

9

while の場合、だいたいブロックの外で宣言される変数を使用する

コピー&ペーストや要素変数名の再利用でバグを織り込みやすい

for ループ使用する場合はコンパイル時エラーで検出可能

Iterator<Element> i = c1.iterator();while (i.hasNext()) { doSomething((Element) i.next()); }

// バグがあるけど問題なくコンパイルできるIterator<Element> i2 = c2.iterator();while (i.hasNext()) { doSomething((Element) i.next()); } // NoSuchElementException!

for (Iterator<Element> i = c1.iterator(); i.hasNext(); ) {doSomething((Element) i.next());

}

// コンパイル時エラー - シンボル i は解決できないfor (Iterator<Element> i2 = c2.iterator(); i.hasNext(); ) {

doSomething((Element) i2.next());}

Page 10: Effective Java 輪読会 項目45-48

メソッドを小さくする

10

メソッドを小さくして、処理をはっきりさせる

一つのメソッドに複数の処理があって、

一つの処理用のローカル変数が別の処理のコードのスコープ内にある場合

⇒ メソッドを分ける(一つの処理に対して一つのメソッド)

Page 11: Effective Java 輪読会 項目45-48

まとめ

11

ローカル変数のスコープを最小化することで

コード可読性と保守性が向上

誤りの可能性が減る

ローカル変数のスコープを最小限にする技法

ローカル変数が初めて使用される時に宣言する

ローカル変数は初期化子を含んで宣言する

ループの場合、 while ループより for ループを選ぶ

ループ終了後にループ変数の内容が必要ない場合に限る

メソッドを小さくして焦点をはっきりさせる

Page 12: Effective Java 輪読会 項目45-48

Item 46従来の for ループより for-each ループを選ぶ

Page 13: Effective Java 輪読会 項目45-48

イテレーション用の変数隠蔽

13

リリース 1.5 より前のイテレーション

コレクション、ジェネリックがない場合

配列の場合

イテレータやインデックス変数を隠蔽してエラーの機会を排除

配列上限計算は一度だけなので、パフォーマンス上のペナルティはない

// イテレータ変数を宣言以外の部分で間違えてると、コンパイル時に検出できないかもfor (Iterator i = c.iterator(); i.hasNext(); ) {

doSomething((Element) i.next()); }

// インデックス変数を宣言以外の部分で間違えてると、コンパイル時に検出できないかもfor (int i = 0; i < a.length; i++) {

doSomething(a[i]); }

// コレクションと配列をイテレートする好ましいイデオムfor (Element e : c) {

doSomething(e);}

Page 14: Effective Java 輪読会 項目45-48

ネストしたイテレーション

14

for ループでのイテレータ変数の操作で使用ミス例

next メソッドの過剰な呼び出し

⇒ 外側要素を変数で保持するように修正...

for-each ループで実装したほうが簡潔

for (Iterator<Suit> i = suits.iterator(); i.hasNext(); )for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )

decks.add (new Card(i.next(), j.next()); // i が底をついたら NoSuchElementException

// コレクションと配列をイテレートする望ましいイデオムfor (Suit suit : suits)

for (Rank rank : ranks)decks.add (new Card(suit, rank));

for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) {Suit suit = i.next();for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )

decks.add (new Card(suit, j.next()); }

Page 15: Effective Java 輪読会 項目45-48

Iterable インタフェース

15

Iterable インタフェースを実装することで for-each 使用可能に

要素のグループを実装する際、 Collection を実装しなくても、Iterable を実装するように

public interface Iterable<E> {Iterator<E> iterator();

}

Page 16: Effective Java 輪読会 項目45-48

まとめ

16

明瞭性とバグ予防に関して、 for-each ループは for ループより優れた長所提供し、パフォーマンス上のペナルティはない

for-each ループを使用できない3つの状況

フィルタリング:コレクションをイテレートして選択された要素だけ削除する

⇒ イテレータ使用して remove メソッドを呼び出す

変換:リストや配列をイテレートして一部か全部の要素の値を置換する

⇒ リストイテレータか配列インデックス変数が必要

並列イテレーション:複数のコレクションを並列にイテレートする

⇒ イテレータやインデックス変数に対して明示的な制御が必要

Page 17: Effective Java 輪読会 項目45-48

Item 47ライブラリーを知り、ライブラリーを使う

Page 18: Effective Java 輪読会 項目45-48

標準ライブラリーを使おう

18

整数の乱数を生成メソッド例の教訓

本当にいい random メソッドは、いろいろ知らないと作れないので、

標準ライブラリーではすでにやってくれたから、それを使いましょう!

標準ライブラリーを利用する利点

専門家の知識と、以前それを利用した人々の経験を利用することになる

時間を無駄にする必要がなく、目的の課題に集中できる

自分では何もしなくても、時間と共にパフォーマンスが改善されたりする

自分のコードを主流に置くことになる

Page 19: Effective Java 輪読会 項目45-48

標準ライブラリーの知るべき機能

19

主要リリースごとに機能が追加されて、それを知ることは得るものがある

標準ライブラリーの知るべき機能

java.lang

java.util (リリース 1.2 にコレクションフレームワーク追加)

java.util.concurrent (リリース 1.5 にコンカレンシーユティリティ追加)

java.io (ある程度)

ちなみに、リリース 1.7 で追加された機能(の一部)

文字列による switch

ダイアモンド演算子

例外の機能拡張(リソース付き、安全な再スロー、マルチキャッチ)

Page 20: Effective Java 輪読会 項目45-48

まとめ

20

無駄な努力をしないこと

かなり共通だと思えることをする前に、まず標準ライブラリーから使えるものを探す

一般的には、ライブラリーのコードは自分で書いたものより優れていて、時間とともに改善されていく可能性がある

Page 21: Effective Java 輪読会 項目45-48

Item 48正確な答えが必要ならば、float と double を避ける

Page 22: Effective Java 輪読会 項目45-48

float 型と double 型は近似計算

22

科学計算と工学計算のために設計されている

2進数浮動小数点算数

広い範囲の大きさに対して正確な近似を行う

正確な結果を提供しない

⇒ float 型と double 型は、特に金銭計算に適しない

Page 23: Effective Java 輪読会 項目45-48

正確な答えを得るには

23

金銭計算など、正確な答えが必要な場合は、 BigDecimal か int か long を使用する

BigDecimal 型を使用

正確な値は出してくれるけど

基本データの算数型を使用するより不便(演算子は使えない)

遅い

int/long 型を使用

金額に応じて使い分ける

小数点がどこかを自分で把握しておく

Page 24: Effective Java 輪読会 項目45-48

まとめ

24

正確な答えを必要とする計算は float や double を使用しない

代わりに BigDecimal か、 int や long を使用する

BigDecimal を使用する状況

システムに小数点を把握させたい

基本データ型を使用しない不便さとコストを気にしない

丸めを完全に制御したい

数が大きい(18桁を超える)

int や long を使用する状況

パフォーマンスが重要

小数点の把握が気にならない

数が大きすぎない(9桁を超えないならint、18桁を超えないならlong)