Java 基础 —— 万用字符

置顶:小白做笔记,逻辑混乱,轻喷

首先先回顾数组参数是如何多态化运行的

普通数组工作的方式

/**
 * @ Author :cy
 * @ Date   :16:08 2020/7/9
 */
abstract class Animal{
    void eat(){
        System.out.println("animal eating");
    }
}
class Dog extends Animal{
    void bark(){   }
}
class Cat extends Animal{
    void meow(){    }
}

public class TestGenerics1 {
    public static void main(String[] args) {
        new TestGenerics1().go();
    }

    public void go(){
        Animal[] animals = {new Dog(), new Cat(), new Dog()};
        Dog[] dogs = {new Dog(), new Dog(), new Dog()};
        takeAnimals(animals);
        takeAnimals(dogs);
    }
    public void takeAnimals(Animal[] animals){
        for(Animal a : animals){
            a.eat();
        }
    }
}

运行结果:
在这里插入图片描述
(忘记加分隔符。将就看吧)
补充:我们只能调用声明在 Animal 中的方法,因为方法的参数是 Animal 数组,且无需任何类型转换(Animal 数组中有两个子类(Dog,Cat),如果要转换需先判断类型)
以上是数组参数多态化运行

ArrayList 参数多态化运行

对上面的例子稍加修改 ( 展示修改部分 )

public void go(){
        /*Animal[] animals = {new Dog(), new Cat(), new Dog()};
        Dog[] dogs = {new Dog(), new Dog(), new Dog()};
        takeAnimals(animals);
        takeAnimals(dogs);*/

        ArrayList<Animal> animals = new ArrayList<>();
        animals.add(new Dog());
        animals.add(new Cat());
        animals.add(new Dog());
        takeAnimals(animals);
    }
 public void takeAnimals(ArrayList<Animal> animals){
        for(Animal a : animals){
            a.eat();
        }
    }

运行结果:
在这里插入图片描述
问题来了,如果我们使用 ArrayList ,编译器会用过编译吗?通过了可以执行吗?

public void go(){
        /*Animal[] animals = {new Dog(), new Cat(), new Dog()};
        Dog[] dogs = {new Dog(), new Dog(), new Dog()};
        takeAnimals(animals);
        takeAnimals(dogs);*/

        /*ArrayList<Animal> animals = new ArrayList<>();
        animals.add(new Dog());
        animals.add(new Cat());
        animals.add(new Dog());
        takeAnimals(animals);*/
        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        dogs.add(new Dog());
        takeAnimals(dogs);
    }

显然,当我们写 takeAnimals(dogs); 时,编译器会告诉我们参数类型不兼容。

回看我们第一个例子(数组参数多态化运行),在例子中,我们传递了数组dogs,程序并没有报错,还正常执行了。这是为啥???难道在这里使用多态出问题了?按照我对多态的理解,Animal 可以做的事情(例如 eat) ,Dog 也能做,所以对Dog引用调用 eat() 方法应该是没错的。

假设编译器不报错,那么问题来了,如果程序出现了下面这段代码呢?

public void takeAnimals(ArrayList<Animal> animals){
        animals.add(new Cat());
     /*   for(Animal a : animals){
            a.eat();
        }*/
    }

这里就出现问题了,我们在 go() 方法中,传入的是 ArrayList< Dog > 的 dogs, 这里加入 Cat 类型不太合适吧,但是在 takeAnimals() 方法中,参数是这样定义的 :ArrayList< Animal > animals,它加入一个Cat对象到集合中不会有错误吧。

到这里,我们大概就能明白为什么 takeAnimals(dogs); 会报错了
也就是说,如果把方法声明成取用 ArrayList< Animal > ,它就只会取用ArrayList< Animal >参数,ArrayList< Dog >与ArrayList< Cat >都不行


此时,你可能会想,如果我在Dog数组中这么做会出现什么情况呢

public void go(){
        Animal[] animals = {new Dog(), new Cat(), new Dog()};
        Dog[] dogs = {new Dog(), new Dog(), new Dog()};
        takeAnimals(animals);
        takeAnimals(dogs);
}
public void takeAnimals(Animal[] animals){
        animals[0] = new Cat();
        for(Animal a : animals){
            a.eat();
        }
    }

哦吼,我的 idea 此时没有提示错误,是不是他就可以运行呢?

运行结果:
在这里插入图片描述

原因:
数组的类型是在运行期间检查的,但集合的类型检查是在编译期间。

怎样才能实现使用多态化集合参数?(传入Dog类型的集合,并可以调用Animal 的 eat方法,禁止将 与传入的集合 不匹配 的对象 加入 到集合中 )

终于来到重点了——万用字符(是不是觉得前面讲了一堆废话)
先看代码

public void takeAnimals(ArrayList<? extends Animal> animals){
        for(Animal a : animals){
            a.eat();
        }
    }

这时,有人可能会说,你这样不也是还可以加入其它类型的对象?
实际上,在使用带有<?>的声明时,编译器不会让你加入任何东西到集合中



此处插播一个小知识点
在这里插入图片描述



直接看结论
↓ ↓ ↓
在这里插入图片描述
这就是万用字符!

注:

public <T extends Animal> void todo( ArrayList<T> list ){}

public void todo1( ArrayList<? extends Animal> list ){}

这两种写法实现的效果是一样的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值