泛型——通配符类型限定

泛型-通配符类型

 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;}
}
  1. 无限定的通配符类型 ? ,若 T 为 ?
    public void set(?){}
    public <?> get(){}
    无限定的通配符中 set 方法不能被调用,甚至也不能用 Object 调用,? 是不能用来类型匹配的。
    get 方法的返回值只能赋给一个 Object
    Bottle<?> 与 Bottle 本质的不同在于: 可以用任意的Object对象调用原始
    Bottle 类的 set 方法。
  1. 使用上界通配符 <? 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 或它的子类,
    这样赋值子类引用给父类是安全的。
  1. 使用下界通配符 <? 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;}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值