泛型概述

Java 泛型(Generics)是 JDK 5 中引入的一个新特性,提供了编译时类型安全检测机制,允许程序员在编译时检测到非法的类型

为什么需要泛型?

泛型可以统一数据类型<数据类型>
没有泛型的时候,集合如何储存数据?
如果我们没有给集合指定类型,默认认为所有的数据类型都是Object类型
此时可以往集合添加任意的数据类型
带来一个坏处,和多态的弊端一样,我们在获取数据的时候,无法使用他(子类)的特有行为

代码演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1.创建集合对象  
ArrayList list = new ArrayList();

//2.添加数据
list.add(123);
list.add("aaa");
list.add(new Student("zhangsan",123));

//3.遍历集合获取里面的每一个元素
Iterator it = list.iterator();
while(it.hasNext()){
Object obj = it.next();
//多态的弊端是不能访问子类特有的功能
//obj.length();
//强转也会报错
String str = (String) obj;
str.length();
System.out.println(str);

注:Student是自己写的JavaBean类:

1
2
3
4
5
6
7
8
9
10
11
12
class Student{  
private String name;
private int age;

public Student(){

}
public Student(String name,int age){
this.name=name;
this.age=age;
}
}

  • 泛型的好处:
  • 统一数据类型
  • 把运行时期的问题提前到编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来

泛型更深入的知识(细节)

Java中的泛型是伪泛型只在编译期间有效
当数据添加进集合的时候,他会检查数据类型,但是在集合里面存储的时候,还是会当成object处理
取出数据的时候,再根据泛型将数据类型强转回原来的类型
泛型的擦除:在Java文件里面,是真的存在泛型的,但是编译过去成为class以后,这个泛型就不存在了
如果不定义泛型,就默认Object

泛型可以在很多地方定义

  1. 泛型类
  2. 泛型方法
  3. 泛型接口

泛型类

当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型,可以写成:T、E、K、V等

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//这里写一个简易的ArrayList来演示  
public class myArrayList<E> {
Object[] obj = new Object[10];
int size;

/*
E:表示是不确定的类型,该类型在类名后面已经定义了。
e:形参的名字,变量名
*/
public boolean add(E e){
obj[size++] = e;
return true;
}

public E get(int index){
return (E)obj[index];
}

@Override
public String toString(){
return Arrays.toString(obj);
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class testDemo {  
public static void main(String[] args) {
myArrayList<String> list1 = new myArrayList<String>();

list1.add("aaa");
list1.add("bbb");
list1.add("ccc");
list1.add("ddd");

System.out.println(list1);

myArrayList<Integer> list2 = new myArrayList<Integer>();
list2.add(123);
list2.add(456);
list2.add(789);

int i = list2.get(0);
System.out.println(i);

System.out.println(list2);
}
}

泛型方法

方法的数据类型不确定时可以使用泛型

  • 可以在类名后面加泛型,这样类里面所有方法都是泛型
  • 也可以单独在方法的修饰符后面加泛型,这样这个就是泛型方法

这里写一个简易的增加多个元素的方法作为演示

1
2
3
4
5
public static<E> void addAll(ArrayList<E> list, E...e){  
for (E e1 : e) {
list.add(e1);
}
}

测试类:

1
2
3
4
5
6
7
8
9
//测试类  
public class genericityMethod {
public static void main(String[] args) {

ArrayList<String> list1 = new ArrayList<>();
myListUtil.addAll(list1,"aaa","bbb","bbb","bbb","bbb","bbb","bbb");
System.out.println(list1);
}
}

  • 泛型接口,如何使用泛型接口
  • 1.实现类给出具体类型
  • 2.实现类延续泛型,创建对象时再确定

1.实现类给出具体的类型 :

1
public class myArrayList2 implements List<String>

2.实现类延续泛型,创建实现类对象时再确定类型

1
public class myArrayList3<E> implements List<E>

泛型与继承性

泛型不具备继承性,但是数据具备继承性

继承结构:

1
2
3
class ye{}  
class fu extends ye{}
class zi extends fu{}

写一个method

1
public static void method(ArrayList<ye> list ){}

测试继承性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {  
/// 泛型不具备继承性,但是数据具备继承性
//创建集合的对象
ArrayList<ye> list1 = new ArrayList<>();
ArrayList<fu> list2 = new ArrayList<>();
ArrayList<zi> list3 = new ArrayList<>();

//调用method发现数据为子类的对象无法传参
method(list1);
method(list2);
method(list3);

//但是数据具备继承性
list1.add(new ye());
list1.add(new fu());
list1.add(new zi());
}

泛型的通配符

如果我们明确了类型,我们就只能传递那一种类型,如果我们不确定传递什么,那么我们可以使用泛型

1
public static<E> void method1(ArrayList<E> list){}

弊端:什么都可以传递进来
如果方法不确定类型,但是只希望能传递Ye Fu Zi
此时我们就可以使用泛型的通配符:

  • ?也表示不确定的类型
  • 他可以进行类型的限定
  • ? extends E:表示可以传递E或者E所有的子类类型
  • ? super E:表示可以传递E或者E所有的父类类型

应用场景:

  • 1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口
  • 2.如果类型不确定、但是能知道以后只能传递某个继承体系中的,就可以用泛型的通配符
    泛型的通配符:
  • 关键点:可以限定类型的范围。
1
public static void method2(ArrayList<? extends ye> list){}