如何理解 java 中的泛型?

1.ArrayList 与 ArrayList<T>的区别

ArrayList 本质是个存 Object 的数组.

  1. 如果定义的时候不写类型. 那么取出的时候就需要强转类型(大多时候需要转为同一类型).
  2. 存入时编译器无法检查类型, 任何对象都能存进去. 取出时强转成一个类型容易出错.
1
2
3
4
5
6
7
8
ArrayList list = new ArrayList();

list.add("Hello");
list.add(new Integer(1));

for (Object o : list) {
String s = (String) o; // 报错 java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
}

这时就想到:

如果有一个专门存String的list就方便了.

如果有一个专门存Integer的list就方便了.

如果有一个专门存自定义对象的list就方便了…

因此就有了 ArrayList<T> List = new ArrayList<T>();

T代表不变的某一类型. 定义的时候就确定了. 存的时候java帮你检查. 取的时候直接取.

子曰:”当断不断, 反受其乱.” 大概讲的就是这个道理.^_^

2. List<T> 与 List<Object>的区别

1
2
3
4
5
6
7
需求: 

业务代码中, 经常遇到判断不同list是否为空, size是否为0的需求.

这时需要检测的list可能是 List<药品库存>, List<药品信息>, List<处方明细>. 这是变化的.

如果要写一个List通用检测方法. 如何定义形参?

2.1 使用List<T>

T 只能代表某一类对象. 需求的对象是变化的. 所以首先排除.

2.2 使用List<Object>

java任何对象都继承自Object好像没有问题. 实际操作会发现报错了.

> 这是因为泛型是不可变。即对于任何2个不同类型的type1和type2,List<Type1>即不是List<Type2>的子类型,也不是List<Type2>的超类型。

简单的说String和Object是父子关系,但是List<String>和List<Object>之间没有继承关系。

2.3 使用List<?>

List<String>和List<Object>之间没有继承关系,但是考虑到代码的通用性,我们又希望有一种类型,可以兼容List<String>和List<Object>。所以泛型里提供了”?”统配符

1
2
List<?> list1 = new ArrayList<Object>();
List<?> list2 = new ArrayList<String>();
  • “?” 表示无上下界
  • “? extends classA” 表示以classA为上界,即以classA为父类
  • “? super classA” 表示以classA为下界,即以classA为子类

因此形参定义成 List<?>即可

3. List<Object> 和 List<?> 有何区别?

1
2
List<Object> list1 = new ArrayList<Object>();
List<?> list2 = new ArrayList<String>();

List<Object> 接收泛型是Object的List对象,
List<?> 接收泛型是任意类型的List对象。