概念

允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)
从JDK1.5以后,Java引入了参数化类型(Parameterized type)的概念,允许我们在创建集合时再指定集合元素的类型,正如:List,这表明 该List只能保存字符串类型的对象。
JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持, 从而可以在声明集合变量、创建集合对象时传入类型实参。
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException异常。同时,代码更加简洁、健壮。

泛型类

interface List 和 class GenTest 其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。
常用T表示,是Type的缩写。

  1. 修饰符 class 类名称<泛型标识> {
  2. 修饰符 泛型标识 // 成员变量类型
  3. 修饰符 构造函数(泛型标识 参数)
  4. }
  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
  2. 泛型类的构造器如下

    1. public GenericClass(){}

    而下面是错误的:

    1. public GenericClass<E>(){}
  3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。

  4. 泛型不同的引用不能相互赋值。 >尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有 一个ArrayList被加载到JVM中。
  5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价 于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
  6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
  7. jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();
  8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换
  9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态 属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法 中不能使用类的泛型。
  10. 异常类不能是泛型的
  11. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity]; 参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。

12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型。
按照约定,类型参数名称命名为单个大写字母,以便可以在使用普通类或接口名称时能够容易地区分类型参数。以下是常用的类型参数名称列表 -

  • E - ELement(元素),主要由Java集合(Collections)框架使用。
  • K - Key(键),主要用于表示映射中的键的参数类型。
  • V - Value(值),主要用于表示映射中的值的参数类型。
  • N - Number(数字),主要用于表示数字。
  • T - Type(类型),主要用于表示第一类通用型参数。
  • S - 类型,主要用于表示第二类通用类型参数。
  • U - 类型,主要用于表示第三类通用类型参数。
  • V - 类型,主要用于表示第四个通用类型参数。

    集合

    只有指定类型才可以添加到集合中:类型安全 读取出来的对象不需要强转:便捷。
  1. class ArrayList<E>{
  2. public boolean add(E e){ }
  3. public E get(int index){ }
  4. ....
  5. }

ArrayList集合:

  1. ArrayList<Integer> list = new ArrayList<Integer>();
  2. list.add(1);
  3. list.add(3);
  4. list.add(5);
  5. Iterator<Integer> iterator = list.iterator();
  6. while (iterator.hasNext()){
  7. System.out.println(iterator.next());
  8. }

HashMap

  1. HashMap<String, Integer> map = new HashMap<String, Integer>();
  2. map.put("one",1);
  3. map.put("two",2);
  4. map.put("three",3);
  5. Set<Map.Entry<String, Integer>> entries = map.entrySet();
  6. Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
  7. while (iterator.hasNext()){
  8. Map.Entry<String, Integer> next = iterator.next();
  9. System.out.println(next.getKey()+":"+next.getValue());
  10. }

请求结果返回定义类
image.png

泛型方法

定义

  1. 修饰符 <泛型标识> 返回值类型 方法名(参数列表(使用泛型)) {
  2. 方法体
  3. }

eg

  1. public class GenericsFunc {
  2. public static <T> String foo(T t) {
  3. return "hello "+t;
  4. }
  5. public static void main(String[] args) {
  6. System.out.println(foo("world"));
  7. System.out.println(foo(123));
  8. System.out.println(foo(false));
  9. }
  10. }

这里就是泛型方法的标识符

泛型接口

与泛型类的用法基本相同,常用于数据类型的生产工厂接口中
定义泛型接口
image.png
继承泛型接口
image.png

创建测试函数
image.png

通配符

泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用问号?,?表示未知通配符,即表示任意的数据类型
注意:此时只能接受数据,不能往该集合中存储数据。
1.:List<?> ,Map<?,?> List<?>是List、List等各种泛型List的父类。
2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型 是什么,它包含的都是Object。
3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
唯一的例外是null,它是所有类型的成员。

通配符受限

在JAVA的泛型中可以指定一个泛型的上限和下限。

泛型的上限

只能接收该类型及其子类

  1. 类型名称 <? extends > 对象名称

只允许泛型为Number及Number子类的引用调用 

  1. <? extends Number>

只允许泛型为实现Comparable接口的实现类的引用调用

  1. <? extends Comparable>

泛型的下限

只能接收该类型及其父类型

  1. 类型名称 <? super > 对象名称

只允许泛型为Number及Number父类的引用调用

  1. <? super Number>

注意点

  1. 不能用在泛型方法声明上,返回值类型前面<>不能使用?
  1. public static <?> void test(ArrayList<?> list){
  2. }
  1. 不能用在泛型类的声明上
  1. class GenericTypeClass<?>{
  2. }
  1. 不能用在创建对象上,右边属于创建集合对象
  1. ArrayList<?> list = new ArrayList<?>();