EffectiveJava読書会4日目

#ジェネリクス
##項目23 新たなコードで原型を使用しない
```java:Collection.java
 //悪い例
 private final Collection stamps =...;
 //良い例
 private final Collection<Stamp> stamps = ...;
 }
```

原型がだめな理由

- 誤ったオブジェクトを挿入→実行時にClassCastException

ジェネリク型の利点

- 誤ったオブジェクトを挿入→コンパイル時にエラー
- 要素を取り出す際にキャストする必要がない

---

###要素がわかっていない場合は非境界ワイルドカードを利用する

ジェネリックを利用したいけど、パラメータが何かわからない
→非境界ワイルドカードを利用する

```java:Collection.java
 //悪い例
 static int numElementsInCommon(Set s1, Set s2){
     int result = 0;
     for (Object o1 : s1)
         if (s2.contains(o1))
             result ++;
     return result;
 }
```

- Setだったらどんな要素でも挿入可
- Set<?>にはいかなる要素も挿入不可

###結論
原型を利用せずにジェネリック型を利用する

##項目24 無検査警告を取り除く

(経験が浅いうちは)ジェネリックを多用すると無検査キャスト警告が多発します

- 取り除くことが可能なら無検査警告を取り除く
- 警告を取り除くことができない、でも型安全であることは明確、@SuppressWarnings("unchecked")アノテーションで警告を抑制する
- @SuppressWarnings("unchecked")アノテーションを利用する場合、スコープはできるだけ小さくする
- 安全である理由をコメントとして残す


##項目25 配列よりリストを選ぶ

###配列

- 共変<br>
SubがSuperのサブタイプならSub[]がSuper[]のサブタイプ
- 具象化<br>
実行時に型要素を既知かつ強制

###リスト

- 不変
- イレイズ<br>
コンパイル時のみ要素の型を強制、実行時には型情報を破棄



```java:ArrayAndAllayList.java
 //配列の場合 実行時に失敗
Object[] objectArray = new Log[1];
objectArray[0] = "I don't fit in";

 //リストの場合 コンパイル時に失敗
List<Object> ol = new ArrayList<Long>();
ol.add("I don't fit in");

```
---

リストはコンパイル時の型安全性を提供

###結論
配列を利用せずにリストを利用する

###ジェネリック型の配列、パラメータ化された型の配列、型パラメータの配列は許可されていない

`new List<E>[], new List<String>[], new E[]`


##項目26 ジェネリック型を使用する

###自作のクラスをジェネリックにする利点

- 型安全性を保証
- クライアントが自作クラスを利用時に明示的なキャストが不要

###配列で実現されているクラスをジェネリックスに変換する場合

例) 項目6 の`Stack`を`Stack<E>`に変換

####2つの方法

1. Object配列を生成して、ジェネリック型配列へキャスト
1. 型パラメータ配列のフィールドをObject[]にする

##項目27 ジェネリックメソッドを使用する

###クラスと同様メソッドもジェネリックメソッドに変更可能

```java:Union.java
 //原型利用
public static Set union (Set s1, Set s2){
    Set result = new HashSet(s1);
    result,addAll(s2);
    return result;
}


//ジェネリックメソッド
public static <E> Set<E> union (Set<E> s1, Set<E> s2){
    Set<E> result = new HashSet<E>(s1);
    result,addAll(s2);
    return result;
}

```

上記のジェネリックメソッドの制限

- 3つのSetの型が全て同じでなければならない

→コンパイラが型推論できる

インスタンス生成例
`Map<String, List<String>> anagrams = new HashMap<String, List<String>>()`

以下を利用すれば簡単になる
`//ジェネリックstaticファクトリーメソッド
public static <K,V> HashMap<K,V> newHashMap(){
    return new HashMap<K,V>
}`

繰り返しの宣言不要
`Map<String, List<String>> anagrams = newHashMap()`



##項目28 APIの柔軟性向上のために境界ワイルドカードを使用する

```java:Stack.java
 //ワイルドカード未使用
public void pushAll (Iterable<E> src){
    for(E e : src);
        push(e);
}


 //ワイルドカード使用
public void pushAll (Iterable<? extends E> src){
    for(E e : src);
        push(e);
}


 //ワイルドカード未使用
public void popAll (Collection<E> dst){
    while(!isEmpty());
        dst.add(pop());
}


 //ワイルドカード使用
public void pushAll (Collection<? super E> dst){
    while(!isEmpty());
        dst.add(pop());
}

```





##項目29 型安全な異種コンテナーを検討する

What？
ひとつのコンテナで、複数の型の要素を、型安全を保証しつつ取り扱う。

Why？
単一要素を取り扱うコンテナではまかないきれない、柔軟性が必要な場合がある。

Example？
データベースの行は任意の数の多くの列を持っており、型安全な方法でそれらすべての列をアクセスしたい。


呼び出し側

```java:Favorites.java
 //型安全な異種コンテナーパターン
public Class Favorites {
    public <T> void putFavorites (Class<T> type, T instance);
    public <T> get Favorites<Class<T> type>;
}

```

クライアント側

```java:Clents.java
public static void main (String[] args) {

     Favorites f = new Favorites();
     f.putFavorite(String.class, "Java");
     f.putFavorite(Integer.class, 0xcafebade);
     f.putFavorite(Class.class, Favorite.class);

     String favoriteString = f.getFavorite(String.class);
     int favoriteInteger = f.getFavorite(Integer.class);
     Class<?> favoriteClass = g.get(Class.class);
     
}

```

実装

```java:Clents.java
public class Favorites {
    private Map<Class<?>, Object> favorites = 
         new HashMap<Class<?>, Object>();
    
    public <T> void put Favorite(Class<T> type, T instance){
        if (type == null)
            throw NullPointerException ("Type is null");
        favorites.put(type, instance);
    
    public <T> getFavorite(Class<T>, type) {
        return type.cast(favorites.get(type));
    }

}


```

###制限

- 原型
- 具現化不可能型



###型を制御

What
メソッドに渡す型を制限したい場合がある。

How

境界型トークンを導入する。
境界型パラメータか、境界ワイルドカードを利用する。




まとめ

ひとつのコンテナで、複数の型を安全に管理できる。
そのための方法は、キーにClassオブジェクト（＝型トークン）を使用することである。