通配符
泛型的局限性与漏洞
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 ,添加类强制类型转换后可以调用,这与普通类继承规则一样
完整代码:继承示例
- 无论 S 和 T 有什么关系,
Pair<S>和Pair<T>没有什么联系。 - 泛型与原始内容兼容,但是原始内容里的类型参数这个对象无法调用方法
- 泛型里的类型参数可以继承,和类的继承规则一样
? 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); }
示例
完整代码:通配符示例