對泛型的一點理解

本文是針對List<T>的用法和場景:

舉個例子

你有 一個羊羣 和 一個牛羣。
羊羣和牛羣都會遛彎。
羊會 咩咩叫 和 需要餵養
牛會 哞哞叫 和 需要餵養

代碼的樣子

你有 一個羊羣 和 一個牛羣。

public class HerdSheep extends Herd{
    /**
     * 羊羣
     */
    List<Sheep> sheeps;

    public List<Sheep> getSheeps() {
        return sheeps;
    }

    public void setSheeps(List<Sheep> sheeps) {
        this.sheeps = sheeps;
    }

    @Override
    public String toString() {
        return "HerdSheep{" +
                "sheeps=" + sheeps +
                "} " + super.toString();
    }
}
public class HerdCow extends Herd {
    /**
     * 牛羣
     */
    private List<Cow> cows;

    public List<Cow> getCows() {
        return cows;
    }

    public void setCows(List<Cow> cows) {
        this.cows = cows;
    }

    @Override
    public String toString() {
        return "HerdCow{" +
                "cows=" + cows +
                "} " + super.toString();
    }
}

羊羣和牛羣都會遛彎。

public class Herd {
    /**
     * 遛彎
     */
    private String walk;

    public String getWalk() {
        return walk;
    }

    public void setWalk(String walk) {
        this.walk = walk;
    }

    @Override
    public String toString() {
        return "Herd{" +
                "walk='" + walk + '\'' +
                '}';
    }
}

羊會 咩咩叫 和 需要餵養

public class Sheep {
    /**
     * 羊叫
     */
    private String bleat;
    /**
     * 是否被餵過
     */
    private String fed;

    public Sheep(String bleat, String fed) {
        this.bleat = bleat;
        this.fed = fed;
    }

    public String getFed() {
        return fed;
    }

    public void setFed(String fed) {
        this.fed = fed;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "bleat='" + bleat + '\'' +
                ", fed='" + fed + '\'' +
                '}';
    }
}

牛會 哞哞叫 和 需要餵養

public class Cow {
    /**
     * 牛叫
     */
    private String moo;
    /**
     * 是否被餵過
     */
    private String fed;

    public Cow(String moo, String fed) {
        this.moo = moo;
        this.fed = fed;
    }

    public String getFed() {
        return fed;
    }

    public void setFed(String fed) {
        this.fed = fed;
    }

    @Override
    public String toString() {
        return "Cow{" +
                "moo='" + moo + '\'' +
                ", fed='" + fed + '\'' +
                '}';
    }
}

牧羊的孩子要幹活了

如果需要你去把每一隻沒喫飽的牲口餵飽,你怎麼辦?

我是這麼做的:

HerdSheep herdSheep = new HerdSheep();
herdSheep.setWalk("溜過彎了");
Sheep sheep0 = new Sheep("咩咩", "喫飽了");
Sheep sheep1 = new Sheep("咩咩", "沒喫飽");
List<Sheep> sheeps = new ArrayList<>();
sheeps.add(sheep0);
sheeps.add(sheep1);
herdSheep.setSheeps(sheeps);

work(herdSheep);
/**
* 放羊娃幹活:給沒喫飽的牲口餵食
*
* @param herd
*/
public void work(Herd herd) {

    if (herd instanceof HerdSheep) {
        for (int i = 0; i < ((HerdSheep) herd).getSheeps().size(); i++) {
            Sheep sheep = ((HerdSheep) herd).getSheeps().get(i);
            if (sheep.getFed().equals("沒喫飽")) {
                sheep.setFed("吃了兩頓喫飽了");
            }
        }
    } else if (herd instanceof HerdCow) {
        for (int i = 0; i < ((HerdCow) herd).getCows().size(); i++) {
            Cow cow = ((HerdCow) herd).getCows().get(i);
            if (cow.getFed().equals("沒喫飽")) {
                cow.setFed("吃了兩頓喫飽了");
            }
        }
    }
    Log.i("yico", herd.toString());
}

結果:

HerdSheep{
    sheeps=[
        Sheep{
            bleat='咩咩',
            fed='喫飽了'
        },
        Sheep{
            bleat='咩咩',
            fed='吃了兩頓喫飽了'
        }
    ]
}Herd{
    walk='溜過彎了'
}

看,第二隻餓着的羊也喫飽了。
但是我們發現work()方法雖然接收的是Herd,但是裏面的操作確實區分 羊羣(HerdSheep) 和 牛羣(HerdCow)來做,並且做的事情都是對牲口 餵養(fed),這讓我很不爽。
那麼我們要怎麼做呢?

  1. 首先 羊羣(HerdSheep) 和 牛羣(HerdCow)和List要放到Herd內,不然還是沒法對Herd直接操作。
  2. 其次Sheep和Cow的fed方法要抽象到一個父類(Animal)裏,不然也沒法繞開Sheep和Cow對fed操作。

第一次改造

public class Herd {
    /**
     * 遛彎
     */
    private String walk;

    private List<Animal> animals;

    public String getWalk() {
        return walk;
    }

    public void setWalk(String walk) {
        this.walk = walk;
    }

    public List<Animal> getAnimals() {
        return animals;
    }

    public void setAnimals(List<Animal> animals) {
        this.animals = animals;
    }

    @Override
    public String toString() {
        return "Herd{" +
                "walk='" + walk + '\'' +
                ", animals=" + animals +
                '}';
    }
}    
public class HerdSheep extends Herd{

    @Override
    public String toString() {
        return "HerdSheep{} " + super.toString();
    }
}
public class Animal {
    /**
     * 是否被餵過
     */
    private String fed;

    public Animal(String fed) {
        this.fed = fed;
    }

    public String getFed() {
        return fed;
    }

    public void setFed(String fed) {
        this.fed = fed;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "fed=" + fed +
                '}';
    }
}
public class Sheep extends Animal{
    /**
     * 羊叫
     */
    private String bleat;

    public Sheep(String fed, String bleat) {
        super(fed);
        this.bleat = bleat;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "bleat='" + bleat + '\'' +
                "} " + super.toString();
    }
}
HerdSheep herdSheep = new HerdSheep();
herdSheep.setWalk("溜過彎了");
Sheep sheep0 = new Sheep("喫飽了", "咩咩");
Sheep sheep1 = new Sheep("沒喫飽", "咩咩");
List<Animal> sheeps = new ArrayList<>();
sheeps.add(sheep0);
sheeps.add(sheep1);
herdSheep.setAnimals(sheeps);

work(herdSheep);
public void work(Herd herd) {

    for (int i = 0; i < herd.getAnimals().size(); i++) {
        if (herd.getAnimals().get(i).getFed().equals("沒喫飽")) {
            herd.getAnimals().get(i).setFed("吃了兩頓喫飽了");
        }
    }
    Log.i("yico",herd.toString());
}

我們發現,這麼改造後,work()方法簡單多了是嗎?
可是構造有問題,我們在構造羊羣的時候,List內是Animal,這個寫法還是很彆扭的。
爲什麼會這麼彆扭呢?
因爲:
hardSheep的setAnimals方法是父類的,但父類的List內是Animal,因此herdSheep.setAnimals(sheeps)的sheeps必須是List<Animal>類型的,而不能是List<Sheep>
那麼我們怎麼辦呢?用 泛型 T 啊!

第二次改造

public class Herd <T extends Animal>{
    /**
     * 遛彎
     */
    private String walk;

    private List<T> animals;

    public String getWalk() {
        return walk;
    }

    public void setWalk(String walk) {
        this.walk = walk;
    }

    public List<T> getAnimals() {
        return animals;
    }

    public void setAnimals(List<T> animals) {
        this.animals = animals;
    }

    @Override
    public String toString() {
        return "Herd{" +
                "walk='" + walk + '\'' +
                ", animals=" + animals +
                '}';
    }
}
public class HerdSheep extends Herd<Sheep>{

    @Override
    public String toString() {
        return "HerdSheep{} " + super.toString();
    }
}
HerdSheep herdSheep = new HerdSheep();
herdSheep.setWalk("溜過彎了");
Sheep sheep0 = new Sheep("喫飽了", "咩咩");
Sheep sheep1 = new Sheep("沒喫飽", "咩咩");
List<Sheep> sheeps = new ArrayList<>();
sheeps.add(sheep0);
sheeps.add(sheep1);
herdSheep.setAnimals(sheeps);

work(herdSheep);
public void work(Herd herd) {

    for (int i = 0; i < herd.getAnimals().size(); i++) {
        if (((Animal) herd.getAnimals().get(i)).getFed().equals("沒喫飽")) {
            ((Animal) herd.getAnimals().get(i)).setFed("吃了兩頓喫飽了");
        }
    }
    Log.i("yico", herd.toString());
}

結果:

HerdSheep{

}Herd{
    walk='溜過彎了',
    animals=[
        Sheep{
            bleat='咩咩'
        }Animal{
            fed=喫飽了
        },
        Sheep{
            bleat='咩咩'
        }Animal{
            fed=吃了兩頓喫飽了
        }
    ]
}

諾,是不是舒服多了,Herd和HerdSheep這兩個對象給別人用的時候就不會感覺突兀了。
另外HerdSheep extends Herd<Sheep>裏這個<Sheep>不是必寫的,但是寫上可以規範代碼,防止傳入錯誤的數據類型。
比如:

List<Cow> cows = new ArrayList<>();
HerdSheep herdSheep = new HerdSheep();
herdSheep.setAnimals(cows);

如果不寫<Sheep>的話,上面不會報錯,但這樣你就把牛羣放到羊圈咯,哈哈。

代碼請見:點擊下載


注:sheep單複數同型,上文中用到sheeps是爲了區別羊羣和羊,請注意(⊙o⊙)哦!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章