Saul's blog Saul's blog
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)

Saul.J.Wu

立身之本,不在高低。
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)
  • Java入门基础

  • Java核心基础

    • 多线程

    • Java常用类

    • 枚举类与注解

    • Java集合

    • 数据结构与算法

    • 泛型

      • 什么是泛型
        • 向上转型
        • 小结
      • 使用泛型
      • 编写泛型
      • 擦拭法
      • 通配符
    • IO流

    • 网络编程

    • 反射

    • 函数式编程

  • 设计模式

  • Web开发

  • SpringBoot

  • 微服务

  • Elasticsearch

  • 运维

  • 后端
  • Java核心基础
  • 泛型
SaulJWu
2021-01-07

什么是泛型

泛型是一种“代码模板”,可以用一套代码套用各种类型。

java-generics

本节我们详细讨论Java的泛型编程。

在讲解什么是泛型(Generic)之前,我们先观察Java标准库提供的ArrayList,它可以看作“可变长度”的数组,因为用起来比数组更方便。🤞

实际上ArrayList内部就是一个Object[]数组,配合存储一个当前分配的长度,就可以充当“可变数组”:

public class ArrayList {
    private Object[] array;
    private int size;
    public void add(Object e) {...}
    public void remove(int index) {...}
    public Object get(int index) {...}
}
1
2
3
4
5
6
7

如果用上述ArrayList存储String类型,会有这么几个缺点:

  • 需要强制转型;
  • 不方便,易出错。

例如,代码必须这么写:

ArrayList list = new ArrayList();
list.add("Hello");
// 获取到Object,必须强制转型为String:
String first = (String) list.get(0);
1
2
3
4

很容易出现ClassCastException,因为容易“误转型”:

list.add(new Integer(123));
// ERROR: ClassCastException:
String second = (String) list.get(1);
1
2
3

要解决上述问题,我们可以为String单独编写一种ArrayList:

public class StringArrayList {
    private String[] array;
    private int size;
    public void add(String e) {...}
    public void remove(int index) {...}
    public String get(int index) {...}
}
1
2
3
4
5
6
7

这样一来,存入的必须是String,取出的也一定是String,不需要强制转型,因为编译器会强制检查放入的类型:

StringArrayList list = new StringArrayList();
list.add("Hello");
String first = list.get(0);
// 编译错误: 不允许放入非String类型:
list.add(new Integer(123));
1
2
3
4
5

问题暂时解决。

然而,新的问题是,如果要存储Integer,还需要为Integer单独编写一种ArrayList:

public class IntegerArrayList {
    private Integer[] array;
    private int size;
    public void add(Integer e) {...}
    public void remove(int index) {...}
    public Integer get(int index) {...}
}
1
2
3
4
5
6
7

实际上,还需要为其他所有class单独编写一种ArrayList:

  • LongArrayList
  • DoubleArrayList
  • PersonArrayList
  • ...

这是不可能的,JDK的class就有上千个,而且它还不知道其他人编写的class。

为了解决新的问题,我们必须把ArrayList变成一种模板:ArrayList<T>,代码如下:

public class ArrayList<T> {
    private T[] array;
    private int size;
    public void add(T e) {...}
    public void remove(int index) {...}
    public T get(int index) {...}
}
1
2
3
4
5
6
7

T可以是任何class。这样一来,我们就实现了:编写一次模版,可以创建任意类型的ArrayList:

// 创建可以存储String的ArrayList:
ArrayList<String> strList = new ArrayList<String>();
// 创建可以存储Float的ArrayList:
ArrayList<Float> floatList = new ArrayList<Float>();
// 创建可以存储Person的ArrayList:
ArrayList<Person> personList = new ArrayList<Person>();
1
2
3
4
5
6

因此,泛型就是定义一种模板,例如ArrayList<T>,然后在代码中为用到的类创建对应的ArrayList<类型>:

ArrayList<String> strList = new ArrayList<String>();
1

由编译器针对类型作检查:

strList.add("hello"); // OK
String s = strList.get(0); // OK
strList.add(new Integer(123)); // compile error!
Integer n = strList.get(0); // compile error!
1
2
3
4

这样一来,既实现了编写一次,万能匹配,又通过编译器保证了类型安全:这就是泛型。

# 向上转型

在Java标准库中的ArrayList<T>实现了List<T>接口,它可以向上转型为List<T>:

public class ArrayList<T> implements List<T> {
    ...
}

List<String> list = new ArrayList<String>();
1
2
3
4
5

即类型ArrayList<T>可以向上转型为List<T>。

要特别注意:不能把ArrayList<Integer>向上转型为ArrayList<Number>或List<Number>。

这是为什么呢?假设ArrayList<Integer>可以向上转型为ArrayList<Number>,观察一下代码:

// 创建ArrayList<Integer>类型:
ArrayList<Integer> integerList = new ArrayList<Integer>();
// 添加一个Integer:
integerList.add(new Integer(123));
// “向上转型”为ArrayList<Number>:
ArrayList<Number> numberList = integerList;
// 添加一个Float,因为Float也是Number:
numberList.add(new Float(12.34));
// 从ArrayList<Integer>获取索引为1的元素(即添加的Float):
Integer n = integerList.get(1); // ClassCastException!
1
2
3
4
5
6
7
8
9
10

我们把一个ArrayList<Integer>转型为ArrayList<Number>类型后,这个ArrayList<Number>就可以接受Float类型,因为Float是Number的子类。但是,ArrayList<Number>实际上和ArrayList<Integer>是同一个对象,也就是ArrayList<Integer>类型,它不可能接受Float类型, 所以在获取Integer的时候将产生ClassCastException。

实际上,编译器为了避免这种错误,根本就不允许把ArrayList<Integer>转型为ArrayList<Number>。

ArrayList<Integer>和ArrayList<Number>两者完全没有继承关系。

# 小结

泛型就是编写模板代码来适应任意类型;

泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查;

注意泛型的继承关系:可以把ArrayList<Integer>向上转型为List<Integer>(T不能变!),但不能把ArrayList<Integer>向上转型为ArrayList<Number>(T不能变成父类)。

帮我改善此页面 (opens new window)
#泛型#T
上次更新: 2021/01/08, 01:19:19
算法分析
使用泛型

← 算法分析 使用泛型→

最近更新
01
zabbix学习笔记二
02-28
02
zabbix学习笔记一
02-10
03
Linux访问不了github
12-08
更多文章>
Theme by Vdoing | Copyright © 2020-2022 Saul.J.Wu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式