泛型
整理BY:Misayaas
内容来自廖雪峰的官方网站: https://www.liaoxuefeng.com/
什么是泛型
泛型就是定义一种模板,例如ArrayList<T>,然后在代码中为用到的类创建对应的ArrayList<类型>:
| 1 | ArrayList<String> strList = new ArrayList<String>(); | 
在Java标准库中的ArrayList<T>实现了List<T>接口,它可以向上转型为List<T>:
| 1 | public class ArrayList<T> implements List<T> { | 
要特别注意:不能把ArrayList<Integer>向上转型为ArrayList<Number>或List<Number>
==ArrayList
向上转型
使用泛型
使用ArrayList时,如果不定义泛型类型时,泛型类型实际上就是Object
编译器如果能自动推断出泛型类型,就可以省略后面的泛型类型。例如,对于下面的代码:
| 1 | List<Number> list = new ArrayList<Number>(); | 
编译器看到泛型类型List<Number>就可以自动推断出后面的ArrayList<T>的泛型类型必须是ArrayList<Number>,因此,可以把代码简写为:
| 1 | // 可以省略后面的Number,编译器可以自动推断泛型类型: | 
泛型接口
除了ArrayList<T>使用了泛型,还可以在接口中使用泛型
Arrays.sort(Object[])可以对任意数组进行排序,但待排序的元素必须实现Comparable<T>这个泛型接口:
| 1 | public interface Comparable<T> { | 
- String本身已经实现了- Comparable<String>接口
| 1 | class Person implements Comparable<Person> { | 
运行上述代码,可以正确实现按name进行排序
编写泛型
把特定类型String替换为T,并申明<T>:
| 1 | public class Pair<T> { | 
静态方法
泛型类型<T>不能用于静态方法
对于静态方法,我们可以单独改写为“泛型”方法,只需要使用另一个类型即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public T getLast() { ... }
    // 静态泛型方法应该使用其他类型区分:
    public static <K> Pair<K> create(K first, K last) {
        return new Pair<K>(first, last);
    }
}
多个泛型类型
我们希望Pair不总是存储两个类型一样的对象,就可以使用类型<T, K>:
| 1 | public class Pair<T, K> { | 
擦拭法
Java语言的泛型实现方式是擦拭法(Type Erasure)
- 所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的
Java使用擦拭法实现泛型,导致了:
- 编译器把类型<T>视为Object;
- 编译器根据<T>实现安全的强制转型
Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T视为Object处理,但是,在需要转型的时候,编译器会根据T的类型自动为我们实行安全地强制转型
Java泛型的局限:
- <T>不能是基本类型,例如- int,因为实际类型是- Object,- Object类型无法持有基本类型:- 1 - Pair<int> p = new Pair<>(1, 2); // compile error! 
- 无法取得带泛型的Class
- 无法判断带泛型的类型 1 
 2
 3
 4
 5Pair<Integer> p = new Pair<>(123, 456); 
 // Compile error:
 if (p instanceof Pair<String>) {
 }
- 不能实例化T类型
不恰当的覆写方法
有些时候,一个看似正确定义的方法会无法通过编译。例如:
| 1 | public class Pair<T> { | 
换个方法名,避开与Object.equals(Object)的冲突就可以成功编译:
| 1 | public class Pair<T> { | 
泛型继承
一个类可以继承自一个泛型类
在继承了泛型类型的情况下,子类可以获取父类的泛型类型
extends通配符
假设我们定义了Pair<T>:
| 1 | public class Pair<T> { ... } | 
然后,我们又针对Pair<Number>类型写了一个静态方法,它接收的参数类型是Pair<Number>:
| 1 | public class PairHelper { | 
上述代码是可以正常编译的。使用的时候,我们传入:
| 1 | int sum = PairHelper.add(new Pair<Number>(1, 2)); | 
注意:传入的类型是Pair<Number>,实际参数类型是(Integer, Integer)
使用Pair<? extends Number>使得方法接收所有泛型类型为Number或Number子类的Pair类型1
2
3
4
5
6
7
8
9
10
11
12public class Main {
 public static void main(String[] args) {
        Pair<Integer> p = new Pair<>(123, 456);
        int n = add(p);
        System.out.println(n);
    }
    static int add(Pair<? extends Number> p) {
        Number first = p.getFirst();
        Number last = p.getLast();
        return first.intValue() + last.intValue();
    }
- 这种使用<? extends Number>的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型T的上界限定在Number了