浅谈Java泛型

Java基础

浏览数:95

2019-11-1

AD:资源代下载服务

概述

Java泛型是J2SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

有关泛型的基础知识这里就不说了,我们直接来看看Java泛型的高阶用法。

extends

定义

有时候,你会希望泛型类型只能是某一部分类型,其实就是给泛型参数添加一个界限。其定义形式为:
<T extends BoundingType>
一定要非常注意的是,这里的extends不是类继承里的那个extends!两个根本没有任何关联。
在这里extends后的BoundingType可以是类,也可以是接口,意思是说,T是在BoundingType基础上创建的,具有BoundingType的功能。

绑定接口/类

public interface Comparable<T> {
    public boolean compareTo(T i);
}
//添加上extends Comparable之后,就可以Comparable里的函数了
public static <T extends Comparable>  T min(T...a){
    T smallest = a[0];
    for(T item:a){
        if (smallest.compareTo(item)){
            smallest = item;
        }
    }
    return smallest;
}

从这段代码也可以看出,类型绑定有两个作用:
1、对填充的泛型加以限定
2、使用泛型变量T时,可以使用BoundingType内部的函数。

通配符

无边界通配符:?

Point point; 
point = new Point(3,3); 
point = new Point(4.3f,4.3f);
point = new Point(4.3d,4.90d); 
point = new Point(12l,23l); 

各种类型的Point实例,都可以赋值给point了。
这里的?就是无边界通配符。它是一个未知的符号,可以是代表任意的类。

通配符?的extends绑定

我们给通配符加上限定: Point<? extends Number> point;
这里虽然是指派生自Number的任意类型,但大家注意到了没: new Point<Number>()也是可以成功赋值的,这说明包括边界自身。

注意:利用<? extends Number>定义的变量,只可取其中的值,不可修改。看下面代码:

Point<? extends Number> point;
point=new Point<Integer>(3,33);
Number integer_1=point.getX();//编译成功
point.setX(new Integer(222));//编译错误

明显在point.setX(new Integer(122))时报编译错误。但point.getX()却不报错。
这是为什么呢?
首先,point的类型是由Point<? extends Number>决定的,并不会因为point = new Point<Integer>(3,3)而改变类型。
正因为point的类型为 Point<? extends Number> point,那也就是说,填充Point的泛型变量T的为<? extends Number>,这是一个什么类型?未知类型!!!怎么可能能用一个未知类型来设置内部值!这完全是不合理的。
但取值时,正由于泛型变量T被填充为<? extends Number>,所以编译器能确定的是Number肯定是<? extends Number>的父类,所以不报错。
也就是说,编译器,只要能确定通配符类型,就会允许,如果无法确定通配符的类型,就会报错。

通配符?的super绑定

如果说 <? extends XXX>指填充为派生于XXX的任意子类的话,那么<? super XXX>则表示填充为任意XXX的父类!

class Employee {}
class Manager extends Employee {}
class CEO extends Manager {}

List<? super Manager> list;
list = new ArrayList<Employee>();
list.add(new Employee()); //编译错误
list.add(new Manager());//编译成功
list.add(new CEO());//编译成功

编译器无法确定<? super Manager>的具体类型,但唯一可以确定的是Manager、CEO肯定是<? super Manager>的子类,所以肯定是可以add进去的。
但Employee不一定是<? super Manager>的子类,所以不能确定,不能确定的,肯定是不允许的,所以会报编译错误。

总结 ? extends 和 ? super 通配符的特征,我们可以得出以下结论:
◆ 如果你想从一个数据类型里获取数据,使用 ? extends 通配符(能取不能存)
◆ 如果你想把对象写入一个数据结构里,使用 ? super 通配符(能存不能取)
◆ 如果你既想存,又想取,那就别用通配符。

参考:Java泛型

作者:陈污龟