泛型(generic): 即为一般的类型、广泛的类型。
1. 泛型的作用与目的:
如果给集合指定泛型,那么该集合只能存放指定类型的元素。【其中,泛型取值不能为基本类型,为引用类型】
试想,如果没有泛型。那么,集合将可以可以存储任意对象,这些对象在集合中,其类型并不明确。当从集合中取出这些元素,他们都将会被编译为Object类型。想要明确类型,就要进行强制转换,此时就可能会引起 ClassCastException。
/*
会引起异常的代码
*/
private static void method() {
//有一个存放人的集合(未指定泛型)
ArrayList list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
//这里没有却存放了人的一个编号
list.add(9527);
for(Object element : list){
//为了找出长度为4的元素,强转为string,用其length方法
String name = (String) element;
System.out.println(name.length());
}
}
虽说没有泛型不影响写代码,但是这种设计不仅使集合存放的元素类型不明确,也会引起异常增加了编程的复杂性。所以,泛型的出现,目的是为了:将运行时期出现的异常(ClassCastException),提前到编译时期发现,避免类型转换。
其本质上,是一种安全提醒 机制
2. 泛型类
当我们定义一个 ArrayList<Integer> list = new ArrayList<>(); 并且调用其add()方法是,发现其方法参数,与泛型一致。
这是为什么?
集合中的这个机制,就是基于泛型类,典型的泛型类,如: ArrayList.
泛型类语法(如何让一个普通类成为泛型类):
public class GenericClassDemo {
public static void main(String[] args) {
//此时T为String,那么这个对象所有涉及T的均为String
MyList<String> stringMyList = new MyList<>();
//泛型为String,添加的参数为String
stringMyList.add("aaa");
//泛型为String,方法返回值为String
String s = stringMyList.get();
}
}
/*
泛型类:
1. 定义: 类名<泛型的变量名>,任意大小写字母,习惯上用大写字母T 或 E
2. 泛型的赋值 : 必须是引用类型
3. 泛型类的泛型 : 是在使用这个类的指定泛型
*/
class MyList<T>{
T t; //可以看做引用类型,只是还未明确指定
public void add(T t){
this.t = t;
}
public T get(){
return t;
}
}
3. 泛型接口
public class GenericInterfaceDemo {
public static void main(String[] args) {
}
}
//泛型接口(语法与泛型方法一直)
interface MyColl<Q> {
void add(Q q);
}
// 实现方式1:实现类在 继承接口的时候 就已经指定 泛型了,那么这个类就不需要是泛型类
class MyInter implements MyColl<String> {
@Override
public void add(String s) {
}
}
// 实现方式2:保留泛型,但必须在类后面指定泛型,那么这个类就是泛型类
class MyInter2<Q> implements MyColl<Q> {
@Override
public void add(Q q) {
}
}
4. 泛型方法
public class GenericMethodDemo {
public static void main(String[] args) {
MyMethod mm = new MyMethod();
mm.add(1);
Boolean play1 = mm.play(true);
}
}
/**
泛型方法:
定义 : 在返回值 前面 <T>
赋值 : 在方法调用的时候,进行传参
*/
//普通类
class MyMethod{
//普通方法
public void show(Object msg){
}
/*
语法:返回值前加<泛型变量>
*/
public <T>void add(T t){
}
//传什么类型的参数,我就返回什么类型的值 (联动)
public <T>T play(T t){
return t;
}
//你传什么类型的数组,我就返回什么类型的元素 (联动)
public <T>T num(T[] t){
return t[0];
}
}
现在 有两个集合,如何用一个方法,同时遍历这两个集合?
ArrayList<String> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
因为泛型没有“继承”的概念。既: ArrayList<Object> 不是 ArrayList<String> 的父类,两者的本质都是容器,
所以无法将方法参数的泛型指定为Object.
用泛型方法就可以解决:
private static <T>void method0(ArrayList<T> list1 ) {
for(Object t : list1){}
}
5. 泛型通配符
上述问题,除了用泛型方法外,还有种更简便的方法,就是用泛型通配符
/*
泛型通配符 : ?
? --> 指代 任意的引用类型 (是一个范围的概念),泛型方法中 T 是 未知的引用类型
此时 T 被 ? 赋值(任意引用类型) 单一的值
*/
private static void method(ArrayList<?> list) {
for(Object t : list){}
}
当直接使用List<?>这种形式时,既明确明确这个list集合可以是任何泛型list的父类。但还有一种特殊情形,程序不希望这个List<?>是任何泛型的父类,只希望他代表某一类型的父类。
上下限概念:
//定义两个类
class Person{
}
class Student extends Person{
}
//定义一个方法
//上限 : ? extends Person --> Person 及其子类
private static void method000(ArrayList<? extends Person> list) {
for(Object t : list){}
}
public static void main(String[] args) {
//list3的元素 是 list4的元素 父类
// 将list4中的所有元素 添加 list3中
ArrayList<Person> list3 = new ArrayList<>();
list3.add(new Person());
list3.add(new Student());
ArrayList<Student> list4 = new ArrayList<>();
list3.addAll(list4);
}