java – 泛型super/extends小练手

下面通过一个简短有说明价值的小例子,来记录一下JAVA泛型中super、extends的用法和区别。

项目地址:https://github.com/owenliang/java-somewhat/tree/master/src/cc/yuerblog/template

Vector泛型容器定义

为了保持简洁,Vector类直接继承自ArrayList,也就拥有了和ArrayList同样的方法,同时Vector也是一个泛型类。

定义泛型类只需要用到<T>这样的语法,不会再复杂了。

walk方法的本意是要求用户传入一个实现了Walker接口的对象,以便可以得到vector的元素遍历回调。

但是walk方法在声明函数参数的时候会用到更加复杂的泛型语法:<? super T>,它的意思是要求Walker接口必须能够接受T类型或者T类型的父类型作为回调参数,什么意思呢?

如果是Vector<Integer>对象,那么它不仅可以接受Walker<Integer>作为参数,甚至可以将Walker<Number>作为参数,这是因为Integer可以向上转型为Number,所以Walker<Number>同样可以兼容。

我们急需看一下Walker接口的定义。

Walker接口定义

该接口也是泛型的,从而可以支持任何元素类型。

onValue方法没有什么魔法,就是<T>决定的。

实现Walker接口并作为回调

现在我们在创建Vector<Integer>对象,实现Walker<Number>接口,然后结合<? super T>看一下整体是如何工作的:

创建vector时,JAVA可以根据左侧变量类型自动推断,所以右侧只需要一个空的<>即可。

接下来传入了一个Walker<Number>接口的实现,覆写的是onValue(Number value)方法。

这时候我们再去看Vector<Integer>::walk方法的实现:

此时walk方法的参数是:Walker<? super Integer> walker,表示可以接纳Walker<Integer>或者父类的Walker<Number>。

onValue(Number value)回调函数传入Integer类型的元素,是可以完成自动向上转换的,这也是为什么Vector<T>的walk方法应该用super的原因:即只要回调函数能经过向上转型兼容传入的参数即可,Vector没必要要求Walker一定要用T类型,父类也是可以的。

再看一下extends

extends的出发点则不同,我们先看代码:

简单说,我定义了一个Main.walk方法,希望支持对任意继承自Number类型的Vector进行遍历。

所以这里函数参数的声明是Vector<? extends Number>,即:vector里的元素只要是Number的子类即可,我都可以接受,并且我在处理的时候都可以安全的向上转型为Number处理。

总结

JAVA定义泛型类只需要简单<T>语法,而声明函数参数的时候就出现了extends和super的两种视角,理解它们的意图应该是最重要的,上面的例子给了一个很好的说明。

如果文章帮到了你,请您乐于扫码捐赠1元钱,以便支持服务器运转。

发表评论

电子邮件地址不会被公开。