java泛型通配符和类型参数的范围

    |     2015年3月31日   |   Java面向对象高级特性   |     0 条评论   |    1653

泛型里如果不限制类型参数,它可以接受任意已定义的类型。很多时候我们只需要其中一部分,用户传入其他类型会在运行期出错。本节先限制类型参数的范围,再讲通配符 ?。

例如编写一个泛型方法,返回 Integer、Double 等数组中的最大值:元素得能取数值,就不能让 T 是任意类型。

写法 用在哪 含义
<T extends Number> 声明泛型类 / 方法的类型参数 T 只能是 Number 或其子类
Point<?, ?> 使用已经声明好的泛型类型 实参类型任意,避免擦成 Object
? extends Number 通配符上界 Number 及其子类;适合取出
? super Number 通配符下界 Number 及其父类;适合放入

一、限制类型参数的范围

下面这段会编译失败:doubleValue() 是 Number 及其子类的方法,不是所有 T 都有。

public <T> T getMax(T array[]) {
    T max = null;
    for (T element : array) {
        max = element.doubleValue() > max.doubleValue() ? element : max;
    }
    return max;
}

用 extends 限制上限,让 T 只接受 Number 及其子类(Integer、Double、Long 等;Character 不是 Number 的子类)。这里的 extends 不再只表示继承:T 是某类的子类型,或 T 实现了某接口。上界若是类只能写一个;接口可以有多个,用 & 分隔,例如 T extends Number & Comparable<T>。

public <T extends Number> T getMax(T array[]) {
    if (array == null || array.length == 0) {
        return null;
    }
    T max = array[0];
    for (int i = 1; i < array.length; i++) {
        T element = array[i];
        max = element.doubleValue() > max.doubleValue() ? element : max;
    }
    return max;
}

max 不能从 null 起步再调 doubleValue(),否则空数组或第一轮比较会空指针;应从第一个元素开始比。

二、通配符 ?

上一节用泛型类表示坐标,坐标可以是整数、小数或字符串:

class Point<T1, T2> {
    T1 x;
    T2 y;
    public T1 getX() { return x; }
    public void setX(T1 x) { this.x = x; }
    public T2 getY() { return y; }
    public void setY(T2 y) { this.y = y; }
}

若在类外定义 printPoint(Point p),没有写出类型实参时会发生类型擦除,相当于 Point<Object, Object>,和不用泛型差不多。要用通配符表示“任意类型的 Point”:

public class Demo {
    public static void main(String[] args) {
        Point<Integer, Integer> p1 = new Point<Integer, Integer>();
        p1.setX(10);
        p1.setY(20);
        printPoint(p1);

        Point<String, String> p2 = new Point<String, String>();
        p2.setX("东京180度");
        p2.setY("北纬210度");
        printPoint(p2);
    }
    public static void printPoint(Point<?, ?> p) {
        System.out.println("This point is: " + p.getX() + ", " + p.getY());
    }
}

运行结果:This point is: 10, 20;This point is: 东京180度, 北纬210度。

三、通配符的上界和下界

数字坐标和字符串坐标含义不同:数字表示 x/y 轴,字符串表示经纬度。可以再限制通配符范围,让一个方法只接受数字坐标,另一个只接受字符串坐标。

public static void printNumPoint(Point<? extends Number, ? extends Number> p) {
    System.out.println("x: " + p.getX() + ", y: " + p.getY());
}
public static void printStrPoint(Point<? extends String, ? extends String> p) {
    System.out.println("GPS: " + p.getX() + "," + p.getY());
}

运行结果:x: 10, y: 20;GPS: 东京180度,北纬210度。? extends Number 表示只能是 Number 及其子类。String 是 final 类,没有子类,因此 ? extends String 实际上只能是 String 本身。

通配符不但能限制上限,也能限制下限:? super Number 表示 Number 及其父类。一般项目里很少自己设计泛型,这里主要是学会怎么读、怎么用,给后面的集合教程铺路。

一句话总结:声明时用 T extends 卡住类型参数;使用时用 ?、? extends、? super 描述“未知但有范围”的实参,避免擦成裸 Object。

转载请注明来源:java泛型通配符和类型参数的范围
本文链接地址:https://ai.zhousir.top/?p=115
回复 取消