一. 什么是泛型
“泛型”,顾名思义,“泛指的类型”。我们提供了泛指的概念,但具体执行的时候却可以有具体的规则来约束。
二. 为什么要使用泛型
以ArrayList为例,如果我们不用泛型。那么存储Integer我们需要使用IntegerArrayList,存储String需要使用StringArrayList。这样子很麻烦,而且很多部分都重复操作了。但如果我们使用泛型,我们只需要传入指定存储的类就好,例如ArrayList
三. 泛型类
1. 模板
1 | public class ClassName<泛型标识> { |
2. 泛型标识
- 对于泛型标识符,可以是字母、中文字符等,不过一般用大写英文字符。一般有一些约定俗称的写法。
- 如果是表示集合的元素类型,一般用字符E,如
public class ArrayList<E>
。 - 如果是表示关键字和值的类型,一般用字符K和V,如
public class HashMap<K,V>
。 - 如果是表示一个类型,一般用字符T,如
public class Person<T>
。
1 | //车库 |
3. 如何使用泛型
1 | public class Person<T> { |
1 | //正常用法 |
四. 泛型接口
1. 模板
1 | public interface Person<T> { |
当我们定义了一个类要实现该接口时,那么该类的泛型类型必须和接口类的泛型类型一致,未传递实参的情况下,继续使用泛型类型T,传递了实参的情况下,泛型类型必须使用实参类型。
1 | public class Teacher<T> implements Person<T> { |
1 | //Teacher不必再定义类型了,因为泛型类型在Person处已经定义好了 |
五. 泛型方法
1.介绍
泛型方法可以定义在普通类和泛型类中,比泛型类更为常用,一般能用泛型方法解决的问题优先使用泛型方法而不使用泛型类,类型变量放在修饰符的后面,如public static ,public final等的后面。
1 | public class Teacher { |
这个时候大家可能会有疑问,返回值返回T不就行了吗,为什么要写成
2. 区别
定义在泛型类中的泛型方法的泛型变量之间是没有关系的。
1 | public class Teacher<T> { |
类泛型类型为String,方法的泛型类型为Integer,虽然都是用T来表示的。
六. 限定类型变量
不论是泛型类还是泛型方法,目前来说其实都是没有做类型限定,无论我们传递什么样类型的变量进去都可以。假如我们想传递的参数类型仅仅是某个大类(父类)下面的一些小类(子类),那么怎么做呢?
1 | //限制传入的类需要implements Comparable并且implements Serializable |
此处用的是extends关键字,extends在这里是表示的是绑定了Comparable接口及其子类型,是“绑定、限定”的意思,非“继承”的意思,后面也可以是接口或者类,如果有多个限制,可以使用&分隔。
七. 泛型通配符
1 | //定义了一个书籍类 |
1. 上界通配符
1 | Bookcase<? extends Novel> bc = new Bookcase<Novel>(new Novel()); |
使用 extends 关键字。
含义是该书柜只能放置小说类书籍(如什么都市小说、爱情小说、玄幻小说都可以),但不能放置父类书籍、其他类如史书、职场类书籍、财经类书籍等。
特点,对上有限制,根据java多态向上造型的原则,不适合频繁插入数据,适合频繁读取数据的场景。
2. 下界通配符
1 | Bookcase<? super Novel> bc = new Bookcase<Novel>(new Novel()); |
使用 super 关键字。
含义就是书柜放置设置了下限,我们只能放置Book书籍以及Novel书籍,却无法再将细分的都市小说、爱情小说类书籍放进去。
特点,和上界通配符正好相反,不适合频繁读取数据,适合频繁插入数据的场景。
3. 无限定通配符
1 | List<?> list = new ArrayList<>(); |
无限定通配符意味着可以使用任何对象,因此使用它类似于使用原生类型。但它是有作用的,原生类型可以持有任何类型,而无限定通配符修饰的容器持有的是某种具体的类型。