0%

corejava基础知识(4)-通配符

本文是对 java 通配符的总结

通配符

泛型的局限性与漏洞

Pair<Employee> employeePair= new Pair<>(new Employee("员工1"), new Employee("员工2"));
Pair<Manager> managerPair= new Pair<>(new Manager("经理1", 100), new Manager("经理2", 200));


//测试1
//employeePair = managerPair; //错误, `Pair<S>`和`Pair<T>`没有什么联系。
//managerPair = employeePair // 错误, `Pair<S>`和`Pair<T>`没有什么联系。


//测试2
Pair pair = employeePair; // 泛型与原始内容兼容,但是,看下面
//pair.getFirs t().getName(); // error, 是Employee类,但是不能调用方法!


//测试3
employeePair.setFirst(new Manager("经理3", 300)); // employeePair里经理竟然和员工组成一对!
System.out.println(employeePair.getFirst().getName()); // 经理3
System.out.println(employeePair.getSecond().getName()); // 员工2
// System.out.println(((employeePair.getFirst())).getSalary()); // error
System.out.println(((Manager)(employeePair.getFirst())).getSalary()); // 300.0 ,添加类强制类型转换后可以调用,这与普通类继承规则一样

完整代码:继承示例

  1. 无论 S 和 T 有什么关系,Pair<S>Pair<T>没有什么联系。
  2. 泛型与原始内容兼容,但是原始内容里的类型参数这个对象无法调用方法
  3. 泛型里的类型参数可以继承,和类的继承规则一样

? extends Object(上边界限定通配符)

可以看出来原始泛型遇上继承时会有些漏洞,比如会出现经理员工在同一Pair的情况。于是Java专家引入了类型通配符 ?

我们把刚刚的第一行改为:

Pair<? extends Employee> employeePair= new Pair<>(new Employee("员工1"), new Employee("员工2"));

此时,如果再向里面添加 Manager时就会发生错误:

employeePair.setFirst(new Manager("经理3", 300)); // 错误
employeePair.setFirst(new Employee("员工")); // 错误,甚至添加员工都不行

但是访问可以:

Employee employee = employeePair.getFirst(); // 正确

分析

首先永远记住只能超类接收子类!!!反过来不行!!!(这个可以解释下面的一切)

类型擦除Pair<? extends Employee> 的方法有:

? extends Employee getFirst() {...} // 访问器
void setFirst(? extends Employee) {...} // 更改器
  • 访问器的返回值是? extends Employee ,可以用子类Employee接收
  • 更改器的接收值是? extends Employee ,极端情况是Employee最下面的子类,而最下面的子类只能接收更下面的子类(无),因此 拒绝接收任何特定的类型!

小结

简单说就是:
可以 Employee <-- ? extends Employee ,但是反过来不行!

所以这就是大家说的使用? extends Object 可以 安全的访问泛型对象。我的理解核心是:如果T作为返回值,用? extends Object更安全。

? super Object(下边界限定通配符)

这个和上面正好相反,更改器能用,访问器不能用。

分析

? super Employee getFirst() {...} // 访问器
void setFirst(? super Employee) {...} // 更改器
  • 访问器的返回值是? super Employee ,极端情况是Object,只能用Object接收
  • 更改器的接收值是? super Employee ,可以接收Employee 和 它的子类

小结

简单说就是:
可以 ? super Employee <-- Employee ,但是反过来不行!

所以这就是大家说的使用? super Object 可以 安全的更改泛型对象。我的理解核心是:如果T作为方法参数,用? super Object更安全。

共同特点

右边的值传给左边接收:

? super Employee <-- Employee <-- ? extends Employee

是不是完全符合 只能超类接收子类,知道原理记起来就简单。

例子

举书上的一个例子作为练习:

public static <T extends Comparable<? super T>> T min(T[] a) //计算T[]的最小值

理解:

  • T extends... 好理解,就是T要实现后面的接口。
  • 实现Comparable<? super T> 接口里的方法int compareTo(? super T);此时类型作为 方法参数,所以用? super更安全。

?(无限定通配符)

? getFirst() {...} // 访问器
void setFirst(?) {...} // 更改器
  • 访问器只能用Object接收
  • 更改器不能用

可以用任意Object对象调用原始 Pair类的setObject方法,说白了就是什么类型都能
作为泛型方法的方法参数,但就是不能有返回值。

通配符捕获

由于通配符不能作为类型变量,所以必要时可以用一个辅助的泛型方法。

第一步:辅助泛型方法

public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}

第二步:通配符捕获

public static void swap(Pair<?> p){ swapHelper(p); }

示例

完整代码:通配符示例