effective java 輪読会 項目49-52

36
Effective Java 輪読会 第7(項目49522014/2/19 開発部 野口

Upload: appresso-engineering-team

Post on 11-Jul-2015

446 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Effective Java 輪読会 項目49-52

Effective Java 輪読会 第7回(項目49~52)

2014/2/19

開発部 野口

Page 2: Effective Java 輪読会 項目49-52

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

Page 3: Effective Java 輪読会 項目49-52

基本データ型とボクシングされた基本データ型の 3 つの違い

基本データ型は値だけを持つが、ボクシングされた基本データ型のインスタンスは値とは別のアイデンティティを持っている

基本データ型は完全に機能する値だけを持っているが、ボクシングされた基本データ型はnull という機能しない値も持っている

基本データ型のほうがボクシングされた基本データ型よりも時間・空間においてより効率的

Page 4: Effective Java 輪読会 項目49-52

不完全なコンパレータ<基本データ型は値だけを持つが、ボクシングされた基本データ型のインスタンスは値とは別のアイデンティティを持っている>

first < second では自動アンボクシングされ、うまくいく

first == second では Integer インスタンスの同一性比較を行う 間違い!

Comparator<Integer> naturalOrder = new

Comparator<Integer>() {

public int compare(Integer first, Integer second) {

return first < second ? -1 : (first == second ? 0 : 1);

}

};

Page 5: Effective Java 輪読会 項目49-52

不完全なコンパレータの修正案

Comparator<Integer> naturalOrder = new

Comparator<Integer>() {

public int compare(Integer first, Integer second) {

int f = first; // 自動アンボクシングint s = second; // 自動アンボクシングreturn f < s ? -1 : (f == s ? 0 : 1); // アンボクシングなし

}

};

Page 6: Effective Java 輪読会 項目49-52

Integer と int との比較<基本データ型は完全に機能する値だけを持っているが、ボクシングされた基本データ型は null という機能しない値も持っている>

Unbelievable は表示……されません

かわりに、(i == 42) で NullPointerException がスローされます

単一操作内で基本データ型とボクシングされた基本データ型を混在させた場合には、ほとんどの場合にボクシングされた基本データ型は自動アンボクシングされる

この場合、i を Integer ではなく int で宣言すれば解決

public class Unbelievable {

static Integer i;

public static void main(String[] args) {

if (i == 42)

System.out.println("Unbelievable");

}

}

Page 7: Effective Java 輪読会 項目49-52

恐ろしく遅いプログラム<基本データ型のほうがボクシングされた基本データ型よりも時間・空間においてより効率的>

public static void main(String[] args) {

long sum = 0L;

for (long i = 0; i < Integer.MAX_VALUE; i++) {

sum += Long.valueOf(1); // ここでオートボクシングが行われる!

}

System.out.println(sum);

}

Page 8: Effective Java 輪読会 項目49-52

ボクシングされた基本データ型を使用すべきとき

コレクション内の要素、キー、値として

より一般的には、パラメータ化された型の型パラメータとして

☓Collection<int>

○ Collection<Integer>

リフレクションによってメソッドを呼び出すとき

参照: 項目53

Page 9: Effective Java 輪読会 項目49-52

まとめ

選択できる場合には、ボクシングされた基本データではなく、基本データ型を使用する

ボクシングされた基本データを使用する場合、注意する == では同一性比較が行われること

ボクシングされた基本データとアンボクシングされた基本データとの計算では、アンボクシングが行われること アンボクシングの際に、NullPointerException をスローする可能性がある

基本データ値をボクシングする際、コストを要する不必要なオブジェクト生成を行う可能性があること

Page 10: Effective Java 輪読会 項目49-52

項目50

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

Page 11: Effective Java 輪読会 項目49-52

文字列は、他の値型に対する代替としては貧弱

外部から入ってきたデータはたいてい文字列だが、より適切な値に変換してから扱うべき

数値なら int、float、BigInteger 等

真偽値なら、boolean

等々

もし適切な型がなければ、適切な値型を書くべき

Page 12: Effective Java 輪読会 項目49-52

文字列は、列挙型に対する代替としては貧弱

enum の方がはるかに優れている

参照: 項目30

Page 13: Effective Java 輪読会 項目49-52

文字列は、集合型に対する代替としては貧弱

よりは、こうしよう

String compoundKey = className + "#" + i.next();

private static class CompoundKey {

private String className;

private String methodName;

...

}

Page 14: Effective Java 輪読会 項目49-52

文字列は、capability に対する代替としては貧弱

ところで、capability って?

Page 15: Effective Java 輪読会 項目49-52

capability って?(1)

capability【名詞】

1【不可算名詞】 [具体的には【可算名詞】]

a〔…の〕能力,才能,手腕,〔…すること〕のできること[能力,才能] 〔for,in,of〕.用例: evaluate the capability of a person 人の才能を評価する.

2【不可算名詞】a(物のもつ)〔…する〕特性,性能 〔for〕.用例the capability of gases for compression ガスの圧縮する性質.

...

Page 16: Effective Java 輪読会 項目49-52

capability って?(2)

Effective Java の例では、「偽造できないキー」のことを capability と呼んでいるようです(pp.218)

「時折、文字列はある機能へのアクセスを与えるために使用されます」(pp.217)

ThreadLocal の例でいうと、「スレッドローカルな変数にアクセスするためのキー」なので、(情報にアクセスする)「能力」の訳語が割と近そうです

でも「能力」じゃやっぱり意味不明なので、capability と呼びます

Page 17: Effective Java 輪読会 項目49-52

(capability の説明に用いられている)ThreadLocal って?

個々のスレッドが固有の値を持つための変数

現在の Java では、以下のようなかたちでライブラリとして提供されている

public final class ThreadLocal<T> {

public ThreadLocal();

public void set(T value);

public T get();

}

Page 18: Effective Java 輪読会 項目49-52

capability として、String の不適切な使用

public class ThreadLocal {

private ThreadLocal() {} // インスタンス化不可能

// 名前付き変数に対するカレントスレッドの値を設定する。public static void set(String key, Object value);

// 名前付き変数に対するカレントスレッドの値を返す。public static Object get(String key);

}

Page 19: Effective Java 輪読会 項目49-52

「capability として、String の不適切な使用」の問題点

キーが共有されたグローバルな名前空間を表している

クライアントが提供した文字列キーが一意でなければならない

2 つの独立したクライアントが同じ名前を使ってしまったら、意図せず変数を共有してしまう

意図して同じ名前を使うこともできてしまう(セキュリティの問題)

Page 20: Effective Java 輪読会 項目49-52

本物の capability

public class ThreadLocal {

private ThreadLocal() {} // インスタンス化不可能

public static class Key { // capability

Key();

}

// 一意の偽造できないキーを生成するpublic static Key getKey() {

return new Key();

}

public static void set(Key key, Object value);

public static Object get(Key key);

}

Page 21: Effective Java 輪読会 項目49-52

ThreadLocal に、もはや Key は不要

set / get をインスタンスメソッドにする

ThreadLocal のインスタンス自体が capability

public class ThreadLocal {

public ThreadLocal();

public void set(Object value);

public Object get();

}

Page 22: Effective Java 輪読会 項目49-52

ThreadLocal を型安全にする

これが(概ね)java.lang.ThreadlLocal が提供している API

public final class ThreadLocal<T> {

public ThreadLocal();

public void set(T value);

public T get();

}

Page 23: Effective Java 輪読会 項目49-52

まとめ

より良いデータ型が存在する、あるいは書くことができる場合に、何も考えずに文字列でオブジェクトを表現したりしない

間違って文字列を使いがちなのは、基本データ型、enum、集合型(それから、capability)

Page 24: Effective Java 輪読会 項目49-52

項目51

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

Page 25: Effective Java 輪読会 項目49-52

文字列結合のパフォーマンス

文字列結合演算子(+)は、便利

だが、n 個の文字列結合は、O(n^2) となる

なぜなら、文字列は immutable だから

Page 26: Effective Java 輪読会 項目49-52

文字列結合の不適切な使用

public String statement() {

String result = "";

for (int i = 0; i < numItems(); i++)

result += lineForItem(i); // String 結合return result;

}

Page 27: Effective Java 輪読会 項目49-52

String の代わりにStringBuilder を使用する

public String statement() {

StringBuilder b = new StringBuilder(numItems() *

LINE_WIDTH);

for (int i = 0; i < numItems(); i++)

b.append(lineForItem(i));

return b.toString();

}

Page 28: Effective Java 輪読会 項目49-52

まとめ

パフォーマンスが重要でない場合以外には、数個以上の文字列を結合するのに文字列結合演算子を使用しない

代わりに、StringBuilder の append メソッドを使用する

あるいは、文字配列を使用するか、文字列を結合する代わりに 1 つずつ処理する

あえてそうすべき時はほとんどなさそうですが……。

Page 29: Effective Java 輪読会 項目49-52

項目52

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

Page 30: Effective Java 輪読会 項目49-52

オブジェクトを参照するには、クラスよりもインタフェースを使用すべき 適切なインタフェース型が存在するならば、パラメータ、戻り値、変数、フィールドはすべてインタフェース型を使用して宣言されるべき

オブジェクトのクラスを参照する必要がある唯一の場合は、そのオブジェクトを生成するとき

Page 31: Effective Java 輪読会 項目49-52

List インタフェース /

Vector クラスでの例

// 良い - 型としてインタフェースを使用// subscribers は同期されている必要があるList<Subscriber> subscribers = new Vector<Subscriber>();

// 悪い - 型としてクラスを使用Vector<Subscriber> subscribers = new Vector<Subscriber>();

Page 32: Effective Java 輪読会 項目49-52

型としてインタフェースを使用するメリット

List<Subscriber> subscribers = new ArrayList<Subscriber>();

Page 33: Effective Java 輪読会 項目49-52

型としてインタフェースを使用する注意点

コードが、インタフェースの一般契約で要求されていない(が、実装クラスは提供している)機能に依存しているなら、実装クラスを変更するとき、新たな実装がその機能を提供しているかどうかが重要となる

例)「Vector が同期されている」ことに依存するコードを書いていれば、それを ArrayList に置き換えることはできない

そのようなときは、変数を宣言する場所に、その要件をドキュメント化すること!

インタフェースを用いることで型の表現力の一部を捨ててしまっているので、(それを補う)これは重要だと思います

Page 34: Effective Java 輪読会 項目49-52

インタフェースを使用する利点にあずかった一例

java.lang.ThreadLocal

フィールドに Map を持つ

1.3 リリースでは、HashMap を用いていた

1.4 リリースでは、IdentityHashMap に変更することで、より速くなった

Page 35: Effective Java 輪読会 項目49-52

適切なインタフェースが存在しない場合は、クラスでオブジェクトを参照する String や BigInteger などの値クラスより一般的には、具象クラスが関連付けされたインタフェースを持たないとき例)Random

クラスに基づくフレームワークに属する場合例)java.util.TimerTask

インタフェースは実装しているが、そのインタフェースにない特別なメソッドをクラスが提供している場合例)PriorityQueue

Page 36: Effective Java 輪読会 項目49-52

まとめ

ある与えられたオブジェクトが適切なインタフェースを持っているかどうかを調べる

インタフェースを持っていれば、オブジェクトを参照するためにインタフェースを使用して、プログラムをより柔軟にする

インタフェースを持っていなければ、クラス階層中で必要な機能を提供している最も上位のクラスを使用する