CurryPanのブログ

カレーパンとプログラミング

【Java, ジェネリクス】同じインタフェースを実装した型のどれかのListに共通の処理をしたい

同じインタフェースを実装した型が複数あり、それらのうちのどれかの型のリストを取得し、同じ操作をしたいという状況がありまして、ジェネリクスを使う方法でやってみました。

処理概要

・商品リストに含まれる各商品の品名、価格を表示したい。
・商品リストにはパンのリストまたはケーキのリストが入るが、どちらになるかは前処理によって変化する。

実装

インタフェース

/**
* 商品に共通する情報を提供するインタフェース.
*/
public interface Item {

    /**
    * 品名を取得する.
    *
    * @return 品名
    */
    String getName();

    /**
     * 価格を取得する.
     * 
     * @return 価格
     */
    int getPrice();

}

クラス

/**
* パン.
*/
public class Bread implements Item {

    private String name;
    private int price;
    // 以下固有のフィールド

    /**
    * {@inheritDoc}
    */
    @Override
    public String getName() {
        return name;
    }

    /**
    * {@inheritDoc}
    */
    @Override
    public int getPrice() {
        return price;
    }

    // 以下固有のメソッド
}
/**
* ケーキ.
*/
public class Cake implements Item {

    private String name;
    private int price;
    // 以下固有のフィールド
    
    /**
    * {@inheritDoc}
    */
    @Override
    public String getName() {
        return name;
    }

    /**
    * {@inheritDoc}
    */
    @Override
    public int getPrice() {
        return price;
    }
    
    // 以下固有のメソッド
}

処理

List<? extends Item> items = new ArrayList<Item>();
switch (itemType) {
case BREAD :
    items = allItems.getBreads(); // List<Bread>
    break;

case CAKE :
    items = allItems.getCakes(); // List<Cake>
    break;
}

for (Item item : items) {
    System.out.println( 
        "品名:" + item.getName() + " 価格:" + item.getPrice());
}

ジェネリクス

非境界ワイルドカード型 List<?>

まず、List<?>という型があります。非境界ワイルドカード型、というそうです。
?ワイルドカードといい、任意の型を表します。なので、List<?> listにはList<String>であろうがList<Cake>であろうがList<Dog>であろうが、代入することが可能です。

上限付きの境界ワイルドカード型 List<? extends Item>

List<? extends T>は、上限付きの境界ワイルドカード型、というそうです。
型はなんでもいいけど、Tのサブタイプにしてね、という制約があります。
今回だと、List<? extends Item>だったので、Itemを実装したBreadCakeをリストに代入することが可能、ということでした。

おわりに

ジェネリクス便利ですね、ちゃんとわかればもっと使い勝手がよくなりそうですね!
今回さわりだけ見てみまして、お恥ずかしながら非変/共変/反変とか全然わかってないので、しっかり調べてみたいと思います。

参考

qiita.com

ありがとうございました!