1.什么是泛型
泛型:就是接受许多类型;通俗来说就是将类型参数化,你想传什么类型的值都行。
下面详细讲:
2.引出泛型
目标:实现一个类,类中有一个能存放任何数据类型的数组,并且成员方法可以返回对应下标的值。
数组是将相同类型的值聚在一起:int[] array = new int[5]; String[] str = new String[10];
这些数组类型都定死了,只要传其他类型的值就报错。
这时我们想到类都继承于Object,写一个Object类型的数组就可以接收所有类型的值,如:

看到这里,我们意识到一个问题 。我不知道要用什么类型来向下转型呀!这确实可以接收各种类型,但是这也太杂了;一个数组里有各种基本类型、各种引用类型,我们能否指定类对象只能接收哪个(哪些)类呢?------》Java就设计了泛型这一概念!!!
3.语法
java
class 泛型类名称<类型形参列表> {
//这里可以使用类型参数
}
class ClassName<>{
}
class 泛型类名称<类型形参列表> extends 继承类/*这里可以使用类型参数*/{
//这里可以使用类型参数
}
class ClassName<T1,T2,...,Tn> extends ParentClass<T1>{
}
上述图片代码改写如下:
java
class MyArray<T>{
Object[] objects = new Object[10];
public T getElement(int index) {
return (T)objects[index];
}
public void setElement(int index,T val){
objects[index]=val;
}
}
代码解释:
- 类名后的 <T> 代表占位符,表示当前类是一个泛型类。(实际上可以使用任意大写字母)
了解:【规范】类型形参常用的名称有:
E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type
S, U, V 等等 - 第二、第三、第四个类型
3.1泛型类的使用
- 定义泛型类的引用:泛型类<类型实参> 变量名 ;
- 实例化泛型类对象:new 泛型类<类型实参>(构造方法实参); //这里的类型实参不写也行,因为系统会根据引用的类型实参进行推导。
示例:
MyArray<Integer> list = new MyArray<Integer>();
//泛型类型不能是基本类型!!!
3.2泛型类引用的类型
先看下面的实操了解代码:
Test2.main()中的myArray是什么类型的?
因为MyArray继承了Object类,所以我们在打印时,会调用父类的toString()方法;
该方法返回调用引用的类型;
结论:可以看出myArray的类型还是MyArray,不是MyArray<Integer>!!!
小结:
- 泛型是将类型参数化,进行传递
- 使用 <T> 表示当前类是一个泛型类。
- 泛型目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换
4.实操

既然对类对象指定了输入数据的类型,那是否还能输入其他类型的数据?
答案是不能!!

5.擦除机制
泛型到底是怎样进行编译的呢?
其实,泛型是编译时期的一种机制,在运行期间是没有泛型这一概念的。
下图就很直观:

在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。
有同学就要问了:那泛型有什么用呢?
答:编译时自动进行类型检查和转换。
6.泛型的上界
在定义泛型类时,有时需要对传入的类型进行约束。
6.1语法
class 泛型类名称<类型形参 extends 类型边界> {
......
}
例:
java
public class ClassName<E extends Number> {
//......
}
代码解读:只有类型是Number或者Number的子类才能作为E的类型形参。(最多只能是Number类,这就是泛型的上界)
例:

代码解读:String不是Number的子类,所以当将String作为泛型实参时就会报错!!
7.泛型进阶
7.1通配符
? 用于在泛型的使用,即为通配符
一个例子让你明白通配符的作用:
java
class Message<T>{
private T message ;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class TestDemo2 {
public static void main(String[] args) {
Message<String> message = new Message<>() ;
message.setMessage("不上早八!");
func(message);
}
public static void func(Message<String> temp){
System.out.println(temp.getMessage());
}
}
上面的代码问题在于func(),这个方法写死了,当我泛型类型不是String,而是其它就会出问题,图例:

所以我想func()什么泛型类型都能接受,就用到通配符(?)。代码改成如下:
java
public class TestDemo2 {
public static void main(String[] args) {
Message<String> message = new Message<>() ;
message.setMessage("不上早八!");
func(message);
Message<Integer> message2 = new Message<>() ;
message2.setMessage(123);
func(message2); //不报错
}
public static void func(Message<?> temp){//什么泛型类型都能接受了
System.out.println(temp.getMessage());
}
}
7.1.1通配符和<E>的区别
通配符是可以在泛型类之外使用的,但E等大写字母,必须在泛型中使用。(<大写字母>是泛型标识)
7.2通配符的上界
语法:
java
<? extends 上界>
<? extends Number> //解读:只能接收Number或者是Number的子类类型
7.2.2实操
将用下面的关系做示例

示例1:
java
class Biology {
}
class Animal extends Biology {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
class Message<T> { // 设置泛型
private T message ;
public T getMessage() {//获取
return message;
}
public void setMessage(T message) {//设置
this.message = message;
}
}
public class TestDemo {
public static void main(String[] args) {
Message<Dog> message = new Message<>() ;
message.setMessage(new Dog());
fun(message);
Message<Cat> message2 = new Message<>() ;
message2.setMessage(new Cat());
fun(message2);
}
public static void fun(Message<? extends Animal> message){
System.out.println(message.getMessage());
message.getMessage();
}
}
注意1:
通配符可不像<E>,你不能写成 ? element = get....(); 所以你不知道传入了什么泛型类型

注意2:

所以我们在传入时要停下来想想了。
总结: 通配符的上界,不能进行写入数据,只能进行读取数据。
7.3通配符的下界
语法:
<? super 下界>
<? super Number> //解读:只接收Number或者Number的父类
示例·:
通配符的下界与上界截然相反:
java
public static void main(String[] args) {
Message<Animal> message = new Message<>() ;
message.setMessage(new Animal());
fun(message);
Message<Biology> message2 = new Message<>() ;
message2.setMessage(new Biology());
fun(message2);
}
public static void fun(Message<? super Animal> message){//只接受Animal或者它的父类
System.out.println(message.getMessage());
message.setMessage(new Dog());//此时可以修改(传入数据),因为Dog、Cat都是Animal子类
message.setMessage(new Cat());
}

这放随便放,但取就要小心了!!!
7.3总结
总结:通配符上界取随便取,放值要思考;通配符下界放随便放,但取就要注意。

