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

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

【Java】switch構文で”重複ローカル変数”の警告が出た

以下のようにswitchのcase文の中でローカル変数を定義したときのことです。

switch (dog) {
case SHIBA:
    String name = "柴太郎";
    break;

case MAME_SHIBA:
    String name = "豆太郎"; // "重複ローカル変数"の警告
    break;

default:
    break;
}

switch構文内で2回目に出現したString nameにおいて、"重複ローカル変数"の警告が出現しました。どうやらswitch構文内は同一スコープなのでローカル変数は使いまわせるよ、ということみたいです。

したがって、

switch (dog) {
case SHIBA:
    String name = "柴太郎";
    break;

case MAME_SHIBA:
    name = "豆太郎";
    break;

default:
    break;
}

このように同一の変数定義をしないようにするか、それがわかりにくければ

switch (dog) {
case SHIBA: {
    String name = "柴太郎";
    break;
}

case MAME_SHIBA: {
    String name = "豆太郎";
    break;
}

default:
    break;
}

波括弧{}を使用してスコープを明示して、各case内にスコープを限定してやればよいとのことでした。

お恥ずかしながら、{}でスコープが決まるということを今回初めて知りました。
確かにそうですよね~。

【PMD, Java】 Overridable method 'hoge' called during object construction が出た

以下のようなクラスを作ったときのことです。
PMDをチェックすると、

Overridable method 'hoge' called during object construction

という警告が出ていました。

public class Hoge {

    private String fuga
    
    /**
    * コンストラクタ.
    */
    public Hoge(String fuga) {
        setFuga(fuga);
    }

    /**
    * ふがを設定する.
    * fuga ふが
    */
    public void setFuga(String fuga) {
        // 何らかの処理
        this.fuga = fuga;
    }

}

警告の対象はコンストラクタのsetFuga()の呼び出し行でした。

いろいろ調べたところ、コンストラクタの実行時はインスタンスを生成している最中なので、メソッド自体がまだ不安定な状態らしいのです。その状態で呼び出されても、動作を保証できませんよ、ということみたいです。

ということで、コンストラクタ内でクラス内のメソッドを呼び出していることが原因でした。セッターを使わずにフィールドに値を設定するように処理を変更することで対処しました。

初歩中の初歩すぎて検索しても出てこなかったです。
お恥ずかしい。

はじめまして。

プログラミング初学者のかれーぱんです。
日々学んだこと、食べたカレーパンのことを記録するブログにしたいと思っております。

プログラミングについて

もともとWeb系だったのですが、サーバサイドをやってみたくてエンジニアに転職しました。Javaを始めて1年半くらいになりましたが、わからないことだらけです。先輩のすすめでEffective JavaやCode Completeを見てみたのですがちんぷんかんぷんでした。
そんなレベルです。

カレーパンについて

すごくおいしいと思っています。


みなさま、よろしくお願いいたします。