ワイルドカード ?
型が違うだけの同じメソッドを 3 本書くのはつらい
「リストの件数を数えたいだけ」なのに、List<Integer> 用と List<String> 用と List<Double> 用を別々に書く。ジェネリクスを覚えた直後に必ず一度はぶつかる場面です。List<String> を List<Object> の引数に渡すことすらできません。String は Object のサブクラスでも、List<String> は List<Object> のサブクラスにはならない、という決まりがあるからです。
そこで使うのが ? (ワイルドカード) です。List<?> は「中身の型は分からないが、とにかくリスト」を表します。
Java
public static void printSize(List<?> list) {
System.out.println("件数は " + list.size());
}size() は中身の型に関係なく int を返すので、これ 1 本で List<Integer> も List<String> も受けられます。件数を数えるだけなら、中身の型の情報は最初から要らなかったわけです。
取り出した値が Object になって、足せない
素の ? には弱点があります。取り出した要素が Object 扱いになるので、Number のメソッドが呼べません。「数値のリストを受け取って平均を出す」のような処理が書けないのです。
ここで ? extends Number と書くと、「Number かそのサブクラス」まで型が絞られます。
Java
public static double averageOf(List<? extends Number> list) {
double total = 0.0;
for (Number n : list) {
total += n.doubleValue();
}
return total / list.size();
}List<Integer> も List<Double> も渡せて、中では Number として読めます。ただし list.add(1) は書けません。呼び出し側が渡したのが List<Integer> なのか List<Double> なのかコンパイラには分からないので、安全に入れられる型が決まらないからです。
今度は add が通らない
逆に「リストへ値を入れる側」を書きたいときは ? super Integer を使います。「Integer かその親」という意味で、List<Integer> List<Number> List<Object> のどれを渡されても、Integer を入れるぶんには安全です。
Java
public static void fillWithZeros(List<? super Integer> list, int count) {
for (int i = 0; i < count; i++) {
list.add(0);
}
}こちらは書き込みができる代わりに、取り出した値は Object までしか保証されません。
判断は「そのリストから読むだけか、そこへ入れるか」の一点です。読むだけなら ? extends、入れるなら ? super。読み書き両方するなら、ワイルドカードをやめて List<Integer> のような具体型で受けます。
覚え方 Producer Extends, Consumer Super の頭文字を取って PECS と呼ばれます。語呂を思い出すより「読む側か、書く側か」で判断するほうが早いです。
よくある間違い
いちばん多いのは List<?> に add しようとして capture of ? というエラーで止まるパターンです。List<?> に安全に入れられるのは null だけなので、入れたいときは ? super にするか、具体型に戻してください。
もう 1 つ、戻り値の型に ? を使うのも避けてください。List<?> load() のようなメソッドは、呼び出し側が要素を取り出すたびに Object 扱いになって、結局キャストだらけになります。
要件
Solution.countItems(int[] arr)メソッドを実装し、配列の要素数をintで返すことarr.lengthを使って要素数を取得すること (ループでカウントする必要はない)- 戻り値の型は
int、引数の型はint[]のシグネチャを変えないこと
入出力例
countItems([1,2,3]) → 3
countItems([]) → 0
countItems([5]) → 1
countItems([10,20,30,40,50]) → 5