最近写项目用到了<? extends T>和<? super T>,于是我搜了一下他俩的相关解释,看到了这文章java <? extends T>和<? super T>介绍,写的挺好的,最后发现其他文章都是类似的内容,猜想应该是某教程的笔记。
我在这里对这篇文章进行简单总结以及我写一下对java <? extends T>和<? super T>的理解。
总结
<? extends T>
和<? super T>
是Java泛型中的“通配符(Wildcards)”和“边界(Bounds)”的概念。
- <? extends T>:是指 “上界通配符 ”,能接收T类及他的子类
- <? super T>:是指 “下界通配符 ”,能接收T类及他的父类
上界<? extends T>不能往里存,只能往外取
(1)<? extends Fruit>会使往盘子里放东西的set( )方法失效,但取东西get( )方法还有效
(2)取出来的东西只能存放在Fruit或它的基类里面,向上造型。
下界<? super T>不影响往里存,但往外取只能放在Object对象里
(1).使用下界<? super Fruit>会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。
因为规定的下界,对于上界并不清楚,所以只能放到最根本的基类Object中
(2).set( )方法正常。
我的总结和理解
我们先来了解一下等号赋值。
比如定义一个变量:int a = 1;
其实就是a变量分配了4个字节来存放数字1,如果等号右边的数字比4个字节大,他就会内存溢出报错。
我们可以想象一下:等号右边是我们想要装多少水,等号左边是用多大的容器装
其实这类一个值赋给另一个值报错,基本就是底层内存分配不足的问题了。
引用上面文章的水果盘盛水果的例子。
public static void main(String[] args) {
// Plate<Apple> p=new Plate<Apple>(new Apple());
Plate<? extends Fruit> p=new Plate<Apple>(new Apple());
Plate<? super Fruit> p1=new Plate<Food>(new Food());
}
static class Plate<T>{
private T item;
public Plate(T t){item=t;}
public void set(T t){item=t;}
public T get(){return item;}
}
//Lev 1
static class Food{}
//Lev 2
static class Fruit extends Food{}
static class Meat extends Food{}
//Lev 3
static class Apple extends Fruit{}
static class Banana extends Fruit{}
static class Pork extends Meat{}
static class Beef extends Meat{}
//Lev 4
static class RedApple extends Apple{}
static class GreenApple extends Apple{}
为啥会出现<? extends T>和<? super T>?
定义一个“水果盘”,逻辑上水果盘当然可以装苹果。结果是报错。
Plate<Fruit> p=new Plate<Apple>(new Apple());
为啥会报错?
因为他编译过后是看不到泛型的(泛型擦除)
比如一个泛型成员变量private <T> t; ,给泛型赋值Apple,他编译后就是private Apple t;,他是看不到泛型的。
你等号右边泛型为Apple ,右边泛型却不是Apple,鬼知道你那分配的内存够不够装下呀,也懒得给你判断了,就干脆给你报错了。
解决方案是使用 <? extends T>和<? super T>。
Plate<? extends Fruit> p=new Plate<Apple>(new Apple());
为啥使用 <? extends T>和<? super T>能解决上述问题?
java为解决这个问题直接定义规则,使用<? extends Fruit>,所有是Fruit的子类我都能容得下,不是Fruit的子类的在java层直接报错,省得你到底层报内存溢出错误。
他在java层处理后就变成这样了
Plate<Fruit> p=new Plate<Apple>(new Apple());
虽然我们这样编写会报错,但事实上它是可以这样分配内存的(这不就是多态吗),这里也就解释了为啥(<? extends T>不能往里存,只能往外取<只能放在Fruit对象或者他的父类中>)
Plate<? super Fruit> p1=new Plate<Food>(new Food());
同理使用<? super Fruit>,所有是Fruit的父类我都能容得下,不是Fruit的父类的在java层直接报错
他在java层处理后就变成这样了
Plate<Object> p=new Plate<Food>(new Food());
Object是你所有类它爸, 它当然有资格接收你的赋的值了。这里也就解释了为啥(<? super T>不影响往里存,但往外取只能放在Object对象里)
额外知识:
public static void main(String[] args) {
A a = new A1();
System.out.println(a.a);
System.out.println(a.a1);
System.out.println(a.show);
}
public static class A{
int a = 1;
}
public static class A1 extends A{
String a1 = "a1";
public void show(){
System.out.println(a1);
}
}
为啥多态中只能调用父类中的变量和方法?
你想想子类本就是父类的扩展,理应比父类需要更多的内存存储,为啥父类的指针可以指向子类?所以在多态中父类指针只能指向子类继承父类哪些数据,而子类扩展的数据它没有分配内存(有心无力)。参考https://blog.csdn.net/ljlinjiu/article/details/83745483
以上全是我的猜想,只供参考(手动滑稽)