子类list转为父类list

1,准备

1.1,创建父类

public class Animal {
    // 获取经度
    public Integer getLng() {
        return null;
    }

    // 获取纬度
    public Integer getLat() {
        return null;
    }

    // 设置位置信息
    public void setLocation(String location){
        // 不知道干啥
    }
}

1.2,创建子类1:狗

@Data
public class Dog extends Animal{
    // 狗的样子
    private String dogLook = "你是怎么知道它是一只狗的";

    // 经度
    private Integer lng;

    // 纬度
    private Integer lat;

    // 定位
    private String location;

    // @Data 提供了所有属性的getter、setter方法
}

1.3,创建子类2:猫

@Data
public class Cat extends Animal {
    // 猫叫声
    private String catCry = "喵喵";

    // 经度
    private Integer lng;

    // 纬度
    private Integer lat;

    // 定位
    private String location;

    // @Data 提供了所有属性的getter、setter方法
}

1.4,创建子类3:鸟

@Data
public class Bird extends Animal{
    // 鸟的动作
    private String birdFly = "我是一只小小鸟,我要飞得更高";

    // 经度
    private Integer lng;

    // 纬度
    private Integer lat;

    // 定位
    private String location;

    // @Data 提供了所有属性的getter、setter方法
}

2,目标

有一个狗list,里面三只狗,分别是处在不同的经纬度;

有一个猫list,里面三只猫,分别是处在不同的经纬度;

有一个鸟list,里面三只鸟,分别是处在不同的经纬度;

目标通过一个工具类的一个方法,接受动物集合,批量根据经纬度定位;

    // Map<经纬度, 定位>
    public static Map<String, String> locationMap = new HashMap<String, String>() {{
        put("1,1", "北京市朝阳区");
        put("1,2", "北京市庞各庄");
        put("1,3", "北京市清华大学");
    }};

    // 批量获取设置动物们的位置信息
    public static void batchSetLocation(List<Animal> animalList) {
        for (Animal animal : animalList) {
            Integer lng = animal.getLng();
            Integer lat = animal.getLat();
            animal.setLocation(locationMap.get(lng + "," + lat));
        }
    }

3,实现

3.1,创建狗list、猫list、鸟list

    // 创建狗list
    public static List<Dog> createDogList() {
        List<Dog> dogList = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            Dog dog = new Dog();
            dog.setDogLook("狗" + i + 1 + "的样子");
            dog.setLng(1);
            dog.setLat(i + 1);
            dogList.add(dog);
        }

        return dogList;
    }

    // 创建猫list
    public static List<Cat> createCatList() {
        List<Cat> catList = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            Cat cat = new Cat();
            cat.setLng(1);
            cat.setLat(i + 1);
            catList.add(cat);
        }

        return catList;
    }

    // 创建鸟list
    public static List<Bird> createBirdList() {
        List<Bird> birdList = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            Bird bird = new Bird();
            bird.setLng(1);
            bird.setLat(i + 1);
            birdList.add(bird);
        }

        return birdList;
    }

3.2,试错

情况1:父类list直接指向子类list

情况2:子类list强转为父类list

分析错误的原因:

为什么父类可以指向子类,子类可以强转为父类?

参考:https://blog.csdn.net/weixin_36433781/article/details/114857048;https://blog.csdn.net/Izayoir/article/details/118612163;

简单的说,就是在堆里创建子类对象,构造子类对象前会先创建一个父类对象,这个子类对象包含了这个父类对象(使用关键词super来引用),

当栈里创建父类型,指向堆里的这个子类对象时,其实是指向了这个子类对象包含的父类对象;

不过特殊的是,此时调用方法时,只能调用父类对象的方法是可以理解的,

但是不是去父类对象的方法区找方法,而是去当前子类对象的方法区找方法,可能就是为了多态才如此设计,就像此篇准备实现的功能;

言归正传:

为什么父类list不可以指向子类list,子类list不可以强转为父类list?

3.3,为什么父类list不可以指向子类list,子类list不可以强转为父类list?

这个只能用反证法,去证明jdk设计人员的一片良苦用心了;

假如以下代码不报错,即假如父类list可以指向子类list,子类list可以强转为父类list:

    public static void main(String[] args) {
        List<Dog> dogList = createDogList();

        // 假如父类list指向子类list不报错
        List<Animal> animalList = dogList;

        Animal animal = new Animal();
        animalList.add(animal);
    }

这时候栈里面的dogList和animalList都指向了堆里面的同一个list对象:

 
java堆
动物

List<Animal> animalList = 上面那个表格,没有问题;

List<Dog> dogList = 上面那个表格,显然这是不符合最基本的设计原理的,即list的第四个对象,是不能在泛型擦除的时候强转为Dog类型的,

即父类对象不能被强转为子类对象;

 3.4,解决的办法

3.4.1,方法1-在堆里重新创建新对象

    public static void main(String[] args) {
        List<Dog> dogList = createDogList();

        // 方法1
        List<Animal> animalList = new ArrayList<>();
        for (Dog dog : dogList) {
            animalList.add(dog);
        }
        batchSetLocation(animalList);

        // 同方法1
        animalList = new ArrayList<>();
        animalList.addAll(dogList);
        batchSetLocation(animalList);

        // 同方法1
        animalList = new ArrayList<>(dogList);
        batchSetLocation(animalList);
    }

3.4.2,方法2-方法或方法形参使用多态

    // 方法2:批量获取设置动物们的位置信息
    public static <T extends Animal> void batchSetLocation(List<T> animalList) {
        for (Animal animal : animalList) {
            Integer lng = animal.getLng();
            Integer lat = animal.getLat();
            animal.setLocation(locationMap.get(lng + "," + lat));
        }
    }

    public static void main(String[] args) {
        List<Dog> dogList = createDogList();

        // 方法2
        batchSetLocation(dogList);

        System.out.println(dogList);
    }

或者:

    // 同方法2:批量获取设置动物们的位置信息
    public static void batchSetLocation(List<? extends Animal> animalList) {
        for (Animal animal : animalList) {
            Integer lng = animal.getLng();
            Integer lat = animal.getLat();
            animal.setLocation(locationMap.get(lng + "," + lat));
        }
    }

    public static void main(String[] args) {
        List<Dog> dogList = createDogList();

        // 同方法2
        batchSetLocation(dogList);

        System.out.println(dogList);
    }

为什么能这么写,可能也是jdk开发人员为我们设计的一种多态的应用场景,用以处理List等复杂对象。

public static <T extends Animal, A extends Dog> void batchSetLocation(List<T> animalList, List<A> dogList)

4,写在最后

除了继承(extends)可以这样使用,实现接口(implements)也可以这样使用;


posted @ 2022-06-23 16:03  seeAll  阅读(2260)  评论(0编辑  收藏  举报