泛型详解

目的

个人认为最核心的目的就是“少些代码”,将共用逻辑抽象出来,形成“模版”。只不过不同语言、不同平台实现方式不一样、名称不一样。

## Java 泛型

### 示例

class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

public void genericsTest() {
    Box<String> stringBox = new Box<>();
    stringBox.setItem("Hello");
    System.out.println(stringBox.getItem());

    Box<Integer> integerBox = new Box<>();
    integerBox.setItem(123);
    System.out.println(integerBox.getItem());
}

### 前因

Java 泛型是在 JDK 5 中引入的新特

### 编译期

编译期做代码(类型)检查

### 运行期

在运行时自动进行类型转换

类型擦除

编译时将泛型类型替换为它们的限定类型,示例代码如下:

public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

如 T 无限定,则编译后的代码大概如下(即限定为 Object):

public class Box {

    private Object item;

    public void setItem(Object item) {
        this.item = item;
    }

    public Object getItem() {
        return item;
    }
}

class Generics {}

public class Box<T extends Generics> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

如 T 有限定,则编译后的限定为父类(即示例中的 Generics):

public class Box {
    private Generics item;

    public void setItem(Generics item) {
        this.item = item;
    }

    public Generics getItem() {
        return item;
    }
}

    public class Box<T> {}
时至今日,类似 `List list = new ArrayList<>();` 的代码仍然是可以通过编译的

如 ArrayList 源码:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

    ...
    transient Object[] elementData; // non-private to simplify nested class access

    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }

    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

    ...
}

Java 范型的问题

  1. 类型擦除导致类型丢失
  2. Java不允许直接创建泛型类型的数组,如 new T[] 或 new List[]
  3. 不能使用基本类型作为泛型参数
  4. 不能使用 instanceof 检查泛型类型
  5. 不能在静态上下文中使用泛型类型,泛型类的静态成员不能使用泛型类型,因为泛型类型在类加载时并不可用。
  6. 受限的类型推断
  7. 泛型不能用于异常类

以上问题都是由于泛型擦除引发的,上述问题在 C++ 中并不存在。

super

public class MyClass<? super T> { } // 这会导致编译错误
public class MyBox<T> {
    // 使用下界通配符的示例方法
    public void addItems(List<? super T> list, T item) {
        list.add(item); // 允许添加 T 或其子类
    }
}

该代码编译后大概为:

public class MyBox {
    // 由于泛型擦除,addItems 方法不再有 T 的类型信息
    public void addItems(List list, Object item) {
        list.add(item); // 由于类型擦除,参数类型变为 Object
    }
}

## Koltin 泛型

## C++ 泛型

C++ 的模板在编译时为每种具体类型生成对应的代码,这种机制称为“模板实例化”

## C# 泛型

C# 跟 Java 是一样的,在最初的版本并不支持,在 C# version 2.0