对泛型的一点理解

本文是针对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⊙)哦!!!

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