泛型-通配符类型
E - Element (在集合中使用,因为集合中存放的是元素),E是对各方法中的泛型类型进行限制,以保证同一个对象调用不同的方法时,操作的类型必定是相同的。E可以用其它任意字母代替
T - Type(Java 类),T代表在调用时的指定类型。会进行类型推断
K - Key(键)
V - Value(值)
N - Number(数值类型)
通配符类型限定:
0. 无限定通配符: ? - 不确定的java类型,是类型通配符,代表所有类型。? 不会进行类型推断
1. 通配符的超类型限定:
? super T 下界通配符 接收 T 类型或 T 类型的父类
2. 通配符的子类型限定:
? extend T 上界通配符 接收 T 类型或 T 类型的子类
<?> 与 <T> 的区别: 在泛型方法中所有的 <T> 若指定将有明确的类型,而<?>是一个通配符,表示所有类型,类型是不明确的,仅仅是占位符。
// 液体
class Liquid{}
// 饮料
class Drink extends Liquid{}
// 水
class Water extends Liquid{}
// 牛奶
class Milk extends Drink{}
// 可乐
class Cola extends Drink{}
// 瓶子
class Bottle<T>{
private T liquid;
public Bottle(T t){liquid = t;}
public void set(T t){liquid = t;}
public T get(){return liquid;}
}
Milk 是 Drink 的一个子类
那么 Bottle<Milk> 是 Bottle<Drink> 的一个子类吗? 不是!
可以这么来看,瓶子里的东西存在继承关系,但是两个同样的瓶子之间没有继承关系,
所以不能执行一个 Bottle<Drink> bd = new Bottle<Milk>(new Milk()); 语句。
运用上界通配符,我们就能够做到这种赋值
Bottle<? extends Drink> bd = new Bottle<Milk>(new Milk));
此时代码中的上界通配符 <? extends Drink> 初始化时允许接收 任何Drink及它的子类。
利用下界通配符,我们可以这样赋值
Bottle<? super Drink> bd = new Bottle<Liquid>(new Liquid);
此时代码中的下界通配符 <? super Drink> 初始化时允许接收 任何Drink及它的父类。
上下界通配符的优缺点
优点: 不同泛型之间的类型转换更加容易
缺点: 容器的部分功能会失效
举例: 以瓶子为例
// 瓶子
class Bottle<T>{
private T liquid;
public Bottle(T t){liquid = t;}
public void set(T t){liquid = t;}
public T get(){return liquid;}
}
- 无限定的通配符类型 ? ,若 T 为 ?
public void set(?){}
public <?> get(){}
无限定的通配符中 set 方法不能被调用,甚至也不能用 Object 调用,? 是不能用来类型匹配的。
get 方法的返回值只能赋给一个 Object。
Bottle<?> 与 Bottle 本质的不同在于: 可以用任意的Object对象调用原始
Bottle 类的 set 方法。
- 使用上界通配符 <? extends T> , 若 T 为 Drink
public void set(<? extends Drink>){}
public <? extends Drink> get(){}
set 方法失效(无论是 Drink 或是它的子类,都失效),只允许 get ,
set 方法失效的原因在于,当我们用上届通配符<? extends Drink> 初始化 Bottle 时,
我们可以用 Milk 构造一个类, 此时 Bottle 的实际类型是 Bottle,
然而 set 方法却仍旧是 set(<? extends Drink> t) ,编译器只知道需要某个Drink的子类型,
但并不知道实际类型,因此拒绝传递任何特定的类型,因为 ? 是不能用来类型匹配的。
get 方法有效的原因在于,<? extends Drink> get 返回的是 Drink 或它的子类,
这样赋值子类引用给父类是安全的。
- 使用下界通配符 <? super T> , 若 T 为 Drink
public void set(<? super Drink>){}
public <? super Drink> get(){}
get 方法失效,只允许 set ,
set 方法有效的原因在于,编译器无法知道 set(<? super Drink>) 的方法参数具体的类型,
因此当初始化为 Drink 的时候,调用这个方法时不能接受类型为 Water 或 Object 的参数,
只能是 Drink 和 它的 子类对象,(例如,set(Number)和set(Object),都允许子类对象,
set (Drink) 也只允许它本身和子类对象)
get 方法失效的原因在于,我们无法通过 Drink 类型来接收 get 的返回值,
当初始化类型是 Drink 时,若Drink drink = get();
编译器无法确定具体的返回对象是 Drink 还是它的子类,因此赋值给一个Object对象作返回值。
```java
//测试get,set 方法的代码
public class TestLiquid {
public static void main(String[] args) {
Bottle<? super Drink> superB = new Bottle<>(new Drink());
superB.set(new Milk());
superB.set(new Drink());
Drink drink = (Drink) superB.get();
Bottle<? extends Drink> extendB1 = new Bottle<>(new Drink());
Bottle<? extends Milk> extendB2 = new Bottle<>(new Milk());
extendB1.get();
extendB2.get();
Bottle<?> another = new Bottle<>(new Drink());
//another.set(new Drink());
Object o = another.get();
Drink drink1 = (Drink) o;
}
}
// 液体
class Liquid {}
// 饮料
class Drink extends Liquid {}
// 牛奶
class Milk extends Drink {}
// 可乐
class Cola extends Drink {}
// 瓶子
class Bottle<T> {
private T liquid;
public Bottle(T t) {liquid = t;}
public void set(T t) {liquid = t;}
public T get() {return liquid;}
}