# 将旧版代码转换为使用泛型

> 原文： [https://docs.oracle.com/javase/tutorial/extra/generics/convert.html](https://docs.oracle.com/javase/tutorial/extra/generics/convert.html)

之前，我们展示了新旧代码如何互操作。现在，是时候看看“泛化”旧代码的难题了。

如果您决定将旧代码转换为使用泛型，则需要仔细考虑如何修改 API。

您需要确保通用 API 不会过度限制;它必须继续支持 API 的原始合同。再考虑一下`java.util.Collection`的一些例子。预通用 API 如下所示：

```
interface Collection {
    public boolean containsAll(Collection c);
    public boolean addAll(Collection c);
}

```

一个天真的尝试，它将如下：

```
interface Collection<E> {

    public boolean containsAll(Collection<E> c);
    public boolean addAll(Collection<E> c);
}

```

虽然这当然是类型安全的，但它不符合 API 的原始合同。 `containsAll()`方法适用于任何类型的传入集合。只有当传入的集合实际上只包含`E`的实例时，它才会成功，但是：

*   传入集合的静态类型可能不同，可能是因为调用者不知道传入的集合的精确类型，或者可能是因为它是`Collection&lt;S&gt;`，其中`S`是`E`的子类型。
*   使用不同类型的集合调用`containsAll()`是完全合法的。例程应该有效，返回`false`。

在`addAll()`的情况下，我们应该能够添加由`E`子类型的实例组成的任何集合。我们在[通用方法](methods.html)一节中看到了如何正确处理这种情况。

您还需要确保修订后的 API 保留与旧客户端的二进制兼容性。这意味着 API 的擦除必须与原始的，未经过授权的 API 相同。在大多数情况下，这自然会失败，但有一些微妙的情况。我们将研究我们遇到的最细微的案例之一，方法`Collections.max()`。正如我们在[章节中看到的更多有趣的通配符](morefun.html)，`max()`的合理签名是：

```
public static <T extends Comparable<? super T>> 
        T max(Collection<T> coll)

```

这很好，除了这个签名的删除是：

```
public static Comparable max(Collection coll)

```

这与`max()`的原始签名不同：

```
public static Object max(Collection coll)

```

当然可以为`max()`指定这个签名，但是没有完成，所有调用`Collections.max()`的旧二进制类文件都依赖于返回`Object`的签名。

我们可以通过在形式类型参数`T`的边界中显式指定超类来强制擦除不同。

```
public static <T extends Object & Comparable<? super T>> 
        T max(Collection<T> coll)

```

这是使用语法`T1 & T2 ... & Tn`为类型参数赋予 _ 多个边界 _ 的示例。已知具有多个边界的类型变量是绑定中列出的所有类型的子类型。当使用多边界时，边界中提到的第一种类型用作类型变量的擦除。

最后，我们应该记得`max`仅从其输入集合中读取，因此适用于`T`的任何子类型的集合。

这将我们带到 JDK 中使用的实际签名：

```
public static <T extends Object & Comparable<? super T>> 
        T max(Collection<? extends T> coll)

```

在实践中出现任何如此复杂的事情是非常罕见的，但专家图书馆设计师应该准备好在转换现有 API 时仔细考虑。

需要注意的另一个问题是 _ 协变返回 _，即改进子类中方法的返回类型。您不应该在旧 API 中利用此功能。为了了解原因，让我们看一个例子。

假设您的原始 API 具有以下形式：

```
public class Foo {
    // Factory. Should create an instance of 
    // whatever class it is declared in.
    public Foo create() {
        ...
    }
}

public class Bar extends Foo {
    // Actually creates a Bar.
    public Foo create() {
        ...
    }
}

```

利用协变返回，您可以将其修改为：

```
public class Foo {
    // Factory. Should create an instance of 
    // whatever class it is declared in.
    public Foo create() {
        ...
    }
}

public class Bar extends Foo {
    // Actually creates a Bar.
    public Bar create() {
        ...
    }
}

```

现在，假设您的代码的第三方客户端编写了以下内容：

```
public class Baz extends Bar {
    // Actually creates a Baz.
    public Foo create() {
        ...
    }
}

```

Java 虚拟机不直接支持覆盖具有不同返回类型的方法。编译器支持此功能。因此，除非重新编译类`Baz`，否则它将无法正确覆盖`Bar`的`create()`方法。此外，`Baz`必须被修改，因为代码将被写入拒绝 - `Baz`中`create()`的返回类型不是`Bar`中`create()`的返回类型的子类型。