※社内の輪講用メモ。まだ途中。
翻訳が、「ジェネリクス」ではなく、「ジェネリックス」なので、表記を合わせています。
p.n表記は、EffectiveJavaのページ番号です。
前史(ジェネリックス以前)
コレクションから読みだしたすべてのオブジェクトをキャストしていた。(挿入する型を間違えるとキャスト失敗)
現在(ジェネリックス時代)
個々のコレクションに許される型をコンパイラに伝え、コンパイラは自動的にキャストを挿入。誤った型を挿入しようとすると、コンパイル時に判明。
ジェネリックスを理解するために。変性の話。
(この辺参考URLから)
変位 | 
意味 | 
Javaでの例 | 
共変 | 
広い型から狭い型へ | 
A<P>はA<C>のスーパータイプ | 
反変 | 
狭い型から広い型へ | 
 A<P>はA<C>のサブタイプ | 
非変 | 
型を変換できないこと | 
A<P>とA<C>の型に関係はない | 
Javaではすべてのジェネリック型は非変(ジェネリック型変数の変位指定はできない)。
不変であるということは、List<Object>のパラメータに、List<String>を渡すことができない。 
No. 23 新たなコードで原型を使用しない
Javaのジェネリック対応は、過去バージョンとの互換性の維持が前提となっているため、犠牲にしている部分がある。
	- 原型(Raw Type)が使用できること
 
	- 変位指定ができない
 
要素型が不明な場合、原型を使用すると下記のように書いてしまうのはよくない(p.110)
[java]
static int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o1 : s1)
if (s2.contains(o1))
result++;
return result;
}
[/java]
非境界ワイルドカード型で書きなおすと(p.111)
[java]
static int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o1 : s1)
if (s2.contains(o1))
result++;
return result;
}
[/java]
原型を使用しない、の例外2つ
クラスリテラルでは原型を使用しないといけない(非境界ワイルドカード型だったらOK)
instanceofつかうとき
[java]
if ( o instanceof Set ) {
Set m = (Set) o; //Setでキャストする必要がある
}
[/java]
No. 24 無検査警告を取り除く
省略
No. 25 配列よりリストを選ぶ
なぜ配列よりリストを選ぶべきなのか?
配列は実行時の型安全は提供するが、コンパイル時の型安全は提供しない
配列よりリストを選ぶ例(p.119)
[java]
import java.net.InterfaceAddress;
import java.util.ArrayList;
import java.util.List;
public class sample01 {
	//元のコード
	static 
 E reduce(List list, Function f, E initVal) {
			E[] snapshot = (E[]) list.toArray(); //警告
			E result = initVal;
			for(E e : snapshot) 
				result = f.apply(result,  e);
			return result;
		}
	
	//安全にする
	static  E reduce2(List list, Function f, E initVal) {
		List snapshot;
		synchronized(list) {
			snapshot = new ArrayList(list);
		}
		E result = initVal;
		for (E e : snapshot)
			result = f.apply(result, e);
		return result;
	}
	
	interface Function {
		T apply (T arg1, T arg2);
	}
	
}
[/java]
以下次回に持ち越し
No. 26 ジェネリック型を使用する
No. 27 ジェネリックメソッドを使用する
No. 28 APIの柔軟性向上のために、境界ワイルドカードを使用する
No. 29 型安全な異種コンテナを使用する
参考資料