回顾泛型,学习通配符
1.前置知识回顾:
在学习数据结构的预备知识的时候,我们已经初步认识了泛型,所谓泛型就是适用于很多类型,从代码来说就是对类型实现了参数化.
泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型.
语法:
其中,<T>代表占位符,表示当前的类是一个泛型类
class 泛型名称 <类型参数列表> { }
class 泛型名称<T1,T2,T3...>
class 泛型名称<类型参数列表>extends 继承类 { }
class 泛型名称<T1,T2,T3...>extends parentClass<T>{}
一些规范:
E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number T 表示 Type
S, U, V 等等 - 第二、第三、第四个类型
泛型类的使用:
我们在第一个<>里面写上类型即可
泛型类名<引用数据类型> 对象名 = new 泛型类名<引用数据类型>();
泛型的上界:
比如:
class TestGneric <T extends Number>{}这个类表示的泛型类, T一定是Number或者Number的子类
泛型方法:
方法限定符 <类型参数列表> 返回值类型 方法名称(形参列表) {...}
这个玩意就是我们在普通类里面写一个泛型方法我们限定T是要实现Comparable 接口的类型
访问修饰限定符
<T extends 接口<T>> T 方法名 (T 参数) {}
擦除机制:
基本上就是,我们在编译的时候,把泛型擦成了Object
2. 通配符
2.1 通配符的概念
? 用于在泛型的使用,即为通配符
?表示可以接收所有的泛型类型,但是又不能够让用户随意修改。
把T改成?就不报错了,因为这里的 T 是一个未声明的类型参数。编译器不知道 T 是什么类型,因为它没有在 fun 方法的上下文中被定义。如果你想让 fun 方法接受任何类型的 Message,所以使用泛型通配符(?)。
具体代码:
package 泛型进阶;
//TODO 通配符
class Message<T> {
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test {
public static void main1(String[] args) {
Message<String> message = new Message<>() ;
message.setMessage("小白");
fun(message);
Message<Integer> message1 = new Message<>();
message1.setMessage(10);
fun(message1);
}
public static void fun(Message<?> temp){//通配符接受任意类型,
System.out.println(temp.getMessage());
// temp.setMessage(23);//无法设置,因为类型是不确定的
}
}
2.2 通配符的上下界
先介绍几个类以及它们之间的关系
通配符的上下界语法:
? extends 类:设置通配符上限
? super 类:设置通配符下限
1> 通配符的上界
上界,也就是最多不能超过的意思,也就是说<=父类
Apple,Banana都是Fruit的子类,因此可以在fun里面进行传参.
注意一个点:
我们的通配符表示接受任意类型,但是传进来的参数类型是不确定的,因此我们不能修改参数.
也就是此时无法在fun函数中对temp进行添加元素,因为temp接收的是Fruit和他的子类,此时存储的元素应该是哪个子类无法确定。所以添加会报错!但是可以获取元素。
因此: 通配符的上界,不能进行写入数据,只能进行读取数据。
具体代码:
package 泛型进阶;
import java.util.ArrayList;
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
}
class Plate<T> { // 设置泛型
private T message ;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test2 {
public static void main(String[] args) {
//App
Plate<Apple> plate = new Plate<>();
Plate<Banana> plate2 = new Plate<>();
fun(plate);
fun(plate2);
}
//TODO 通配符的上界
public static void fun(Plate<? extends Fruit> temp){
// 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改
//temp.setMessage(new Banana()); //仍然无法修改!
//temp.setMessage(new Apple()); //仍然无法修改!
Fruit fruit = temp.getMessage();//向上转型
System.out.println(fruit);
}
}
2> 通配符的下界
下界,也就是>=的意思,不能小于父类的意思
Food是Fruit的父类,因此可以调用fun2进行传参
这里注意一个点:
通配符的下界,不能进行读取数据,只能写入数据。
具体代码:
package 泛型进阶;
import java.util.ArrayList;
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
}
class Plate<T> { // 设置泛型
private T message ;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test2 {
public static void main2(String[] args) {
ArrayList<Integer> a = new ArrayList<>();
Plate<Fruit> plate = new Plate<>();
Plate<Food> plate2 = new Plate<>();
fun2(plate);
fun2(plate2);
}
//TODO 通配符的下界
public static void fun2(Plate<? super Fruit> temp) {
temp.setMessage(new Apple());
temp.setMessage(new Banana());
//Fruit fruit = (Fruit)temp.getMessage();//传过来的都是父类所以要强转
}
}
小总结:
1. ?一般作为参数类型使用,T一般用于泛型类,泛型方法的定义来使用.
2. ? 一般不晓得确切类型,因此只能读不能写(修改).T一般晓得确切类型,可以读和修改.
3. 通配符的下界,不能进行读取数据,只能写入数据。通配符的上界,不能进行写入数据,只能进行读取数据。
原文地址:https://blog.csdn.net/2201_75880772/article/details/144326957
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!