将泛型类型实现为一种语言

Implementing generic types to a language

我想为一种语言添加对泛型的支持,但在我这样做之前,我需要更清楚地了解其中的泛型。

我目前的理解是实例化 class:

class ArrayList<T>
{
    public int add(T object)
    {
        // ...
    }
}

创建一个实例,其中 addT 的参数类型必须与 new ArrayList<T>() 上的类型参数相同,其中 T 是实型。

实现这个看起来很简单,但仅适用于这个用例。当它需要支持内省时,它就变成了一个更复杂的概念。例如,我不会说:

true === (new ArrayList<Date>() instanceof ArrayList)

但我会说:

true === (new ArrayList<Date>() instanceof ArrayList<Date>)

我的问题是,当使用 ArrayList<Date> 作为类型引用时,它是 ArrayList<T> 的派生类型,还是本身就是 class,例如:

$list = new ArrayList<Date>();

实例:

class ArrayList
{
    public int add(Date $object)
    {
        // ...
    }
}

或者它是 ArrayList<T> 的实例,其中 TDate

泛型只是编译时的帮手。如果我们没有泛型,我们会写这样的代码:

ArrayList list = new ArrayList();
list.add(new Date());
Date date = (Date) list.get(0);

引入了泛型来删除必要的转换,我们现在可以写:

ArrayList<Date> list = new ArrayList<>();
list.add(new Date());
Date date = list.get(0);

你可能认为 ArrayList class 内部管理着一个 Date 的数组,但实际上 它管理一个 Object 数组。编译器会在您访问元素的位置插入缺失的强制转换。

使用泛型还有另一个好处。编译器可以检测您是否进行了无效的转换。所以不可能这样写:

ArrayList<Date> list = new ArrayList<>();
list.add(new Date());
String date = list.get(0);

您可以使用 new ArrayList<String>().getClass().equals(new ArrayList<Date>().getClass()).

验证具有不同类型参数的 ArrayList 仍然编译为相同的 class

看看Oracle Java Documentation about type erasure

Java 泛型和 C++ 模板乍一看很相似,但实现方式却相反。正如 Steffen Kreutz 在评论中所说,java 对其泛型使用类型擦除。类型擦除意味着在编译时,java 控制对泛型 class 的访问,但在 运行 时,所有类型信息都消失了,并且 ArrayList<Date>ArrayList<Integer> 共享完全相同的代码。

这与模板完全不同。在模板中,每个具体的 class 都是在编译时实现的,所以(在 ​​C++ 中)vector<double>vector<char> 是两个不同的 class,它们的编译代码会不同,因为一个将采用双参数,而另一个将采用字符并且它们在堆栈中的传递方式不同。

如果您需要一个泛型 class 来了解它可以接受的类型,您必须明确地使用一个属性来保留它。例如:

class MyGen<T> {
    class<T> myClazz;

    MyGen(class<T> clazz) {
        myClass = clazz;
    }
    ...
}

然后您可以在 myClazz 上使用反射,因为它是一个真正的 class 对象,可在 运行 时使用,而 T 只能在编译时使用,不能通过反射使用。