泛型(二)——通配符和上下界限制

泛型(二)——通配符和上下界限制

泛型的上下界限制一直不明白什么意思,跑了一遍代码终于明白了。

在讨论之前有以下几个类:

//Food.java
public class Food {
    public String toString(){
        return this.getClass().getName();
    }
}

//Fruit.java
public class Fruit extends Food {}

//Apple.java
public class Apple extends Fruit {}

1. 协变

java中数组是协变的,以下语句可以成立:

Fruit[] fruits = new Apple[]{new Apple()};

而泛型不是协变的,我们有这样一个泛型类:

public class AGenericClass <T> {
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

以下的语句也是不成立的:

AGenericClass<Fruit> aGenericClass = new AGenericClass<Apple>();  //Error

也就是说AGenericClass<Apple>AGenericClass是不一样的
为了让容器类满足父类的容器能够容纳子类的需求,就有了边界和通配符的使用。

2. 通配符和边界

泛型本质上就是一个类型参数,也就是把类型作为参数进行使用。在定义形参的时候只能使用extends来限定边界,意味着只接受某类的子类作为类型参数。例如:

class BGenericClass<Apple extends Fruit> {
    private Apple apple;

    public Apple getApple() {
        return apple;
    }

    public void setApple(Apple apple) {
        this.apple = apple;
    }
}

这里的Apple是定义的形参,并不是指真正的类Apple。这里定义的形参把类型参数约束为Fruit的子类。形参不能使用通配符?,通配符只有作为实参输入类型参数的时候才能使用。
实参可以使用通配符?,意味着任何类,也可以配合extendssuper来定义上下边界。例如:

AGenericClass<?> aGenericClass = new AGenericClass<Fruit>();

此时的声明的aGenericClass具有类型参数?,这是一个通配符,不是任何一个确定的类,这个对象的setT(T t)方法要求输入一个?类型的对象,这个?类型不能确定是任何类的基类,因此无法自动向上转型。同时?类型不具有实际的类定义,所以其他类也不能够向下转型成?,因此以类型参数T作为方法参数的set(T t)方法无法使用:

aGenericClass.setT(new Fruit());    //Error

而同理这个对象的getT()方法返回了一个T类型的对象,在编译阶段没有这个对象的类型信息,只能向上转型成为Object对象:

Object fruit = aGenericClass.getT();

在配合使用extendssuper的时候也有类似的情况,分别讨论extendssuper的使用

2.1. extends

extends使得声明的泛型对象可以接受类型参数为子类的泛型对象,如下:

AGenericClass<? extends Fruit> aGenericClass = new AGenericClass<Apple>();

?类似,这样是有代价的:
带有<? extends Fruit>泛型参数的对象无法使用带有类型参数作为方法参数的方法
这句话有点绕,其实意思就是:1. 假如你用了<? extends Fruit>,2. 那么类中定义的方法的参数列表中带有T的你就不能用。即以下的语句是不成立的:

aGenericClass.setT(new Apple());    //Error
aGenericClass.setT(new Fruit());      //Error

也就是说,方法接受的参数是Fruit的子类,编译器不能自动帮你向下转型
带有<? extends Fruit>泛型参数的对象可以使用以类型参数作为返回值的方法
意思就是,假如一个方法返回T,那么这个方法可用。即:

Fruit fruit = aGenericClass.getT(); //ok

getT()方法返回了一个Fruit类型的子类,这个类一定是Fruit或者它的子类,因此可以自动地向上转型成为Fruit

2.2. super

super使得声明的泛型对象可以接受类型参数为父类的泛型对象,如下:

AGenericClass<? super Fruit> aGenericClass = new AGenericClass<Food>();

带有<? super Fruit>泛型参数的对象不可以使用以类型参数作为返回值的方法
即:

Fruit fruit = aGenericClass.getT();   //Error

getT()方法返回了一个Fruit类型的父类,这个父类的具体类型不确定,总之不一定是Fruit,但是它可以向上转型成为Object,或者显式向上转型成为Fruit

Object object = aGenericClass.getT(); //ok
Fruit fruit2 = (Fruit) aGenericClass.getT();   //ok

带有<? super Fruit>泛型参数的对象可以使用带有类型参数作为方法参数的方法
即:

aGenericClass.setT(new Apple());
aGenericClass.setT(new Fruit());

setT(T t)方法要求输入一个是Fruit类型或者它的父类,因此可以保证Fruit可以向上转型成为这个对象的类型。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值