子类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)也可以这样使用;