本文是对 java 通配符的总结
通配符
泛型的局限性与漏洞
Pair<Employee> employeePair= new Pair<>(new Employee("员工1"), new Employee("员工2")); |
完整代码:继承示例
- 无论 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)); // 错误 |
但是访问可以:
Employee employee = employeePair.getFirst(); // 正确 |
分析
首先永远记住只能超类接收子类!!!反过来不行!!!(这个可以解释下面的一切)
类型擦除 后 Pair<? extends Employee>
的方法有:
? extends Employee getFirst() {...} // 访问器 |
- 访问器的返回值是
? extends Employee
,可以用子类Employee
接收 - 更改器的接收值是
? extends Employee
,极端情况是Employee
的最下面的子类
,而最下面的子类
只能接收更下面的子类(无),因此 拒绝接收任何特定的类型!
小结
简单说就是:
可以 Employee
<-- ? extends Employee
,但是反过来不行!
所以这就是大家说的使用? extends Object
可以 安全的访问泛型对象。我的理解核心是:如果T
作为返回值,用? extends Object
更安全。
? super Object(下边界限定通配符)
这个和上面正好相反,更改器能用,访问器不能用。
分析
? super Employee getFirst() {...} // 访问器 |
- 访问器的返回值是
? 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() {...} // 访问器 |
- 访问器只能用
Object
接收 - 更改器不能用
可以用任意Object
对象调用原始 Pair
类的setObject
方法,说白了就是什么类型都能
作为泛型方法的方法参数,但就是不能有返回值。
通配符捕获
由于通配符不能作为类型变量,所以必要时可以用一个辅助的泛型方法。
第一步:辅助泛型方法
public static <T> void swapHelper(Pair<T> p) |
第二步:通配符捕获
public static void swap(Pair<?> p){ swapHelper(p); } |
示例
完整代码:通配符示例