之前,我们展示了新代码和旧代码如何互操作。现在,该看一下“生成”旧代码的难题了。
    如果您决定将旧代码转换为使用泛型,则需要仔细考虑如何修改API。
    您需要确保泛型API不会受到过度限制;它必须继续支持API的原始协议。再次考虑来自java.util.Collection的一些示例。泛型API如下所示:

    1. interface Collection {
    2. public boolean containsAll(Collection c);
    3. public boolean addAll(Collection c);
    4. }

    天真的尝试将其夸大如下:

    1. interface Collection<E> {
    2. public boolean containsAll(Collection<E> c);
    3. public boolean addAll(Collection<E> c);
    4. }

    虽然这肯定是类型安全的,但它不符合API的原始协议。containsAll()方法适用于任何类型的传入集合。仅当传入集合实际上仅包含E的实例时,它才会成功,但是:

    • 传入集合的静态类型可能有所不同,可能是因为调用者不知道传入的集合的确切类型,或者可能是因为它是Collection<S>,其中SE的子类型。
    • 用不同类型的集合调用containsAll()是完全合法的。 该例程应该工作,并返回false

    对于addAll(),我们应该能够添加任何由E的子类型的实例组成的集合E。我们在“泛型方法”小节中介绍了如何正确处理这种情况 。
    您还需要确保修订后的API保留与旧客户端的二进制兼容性。这意味着对API的擦除必须与原始的未经过增强的API相同。在大多数情况下,这自然而然地消失了,但是有一些微妙的情况。我们将研究Collections.max()方法中遇到的最微妙的情况之一。正如我们在“通配符带来更多乐趣”一节中所看到的一样 ,max()合理的签名是:

    1. public static <T extends Comparable<? super T>>
    2. T max(Collection<T> coll)

    可以,但是可以删除此签名:

    1. public static Comparable max(Collection coll)

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

    1. public static Object max(Collection coll)

    当然可以为max()指定此签名,但是并没有完成,并且所有调用Collections.max()的旧二进制类文件都依赖于返回Object的签名。
    我们可以通过在形式类型常数T的边界中显式指定一个超类来强制删除操作不同。

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

    这是使用语法T1 & T2 ... & Tn为类型参数赋予多个界限的示例。已知具有多个界限的类型变量是界限中列出的所有类型的子类型。当使用多重边界时,边界中提到的第一个类型将用作擦除类型变量。
    最后,我们应该记得,方法max仅从其输入集合读取,因此适用于T的任何子类型的集合。
    这使我们了解了JDK中使用的实际签名:

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

    涉及到的任何事情在实践中都是很少见的,但是在转换现有的API时,专家库设计人员应该准备好仔细考虑。
    需要注意的另一个问题是协变返回(covariant returns,即在子类中优化方法的返回类型。您不应该在旧的API中利用此功能。要了解原因,我们来看一个示例。
    假设原始API的格式为:

    1. public class Foo {
    2. // Factory. Should create an instance of
    3. // whatever class it is declared in.
    4. public Foo create() {
    5. ...
    6. }
    7. }
    8. public class Bar extends Foo {
    9. // Actually creates a Bar.
    10. public Foo create() {
    11. ...
    12. }
    13. }

    利用协变返回优势,可以将其修改为:

    1. public class Foo {
    2. // Factory. Should create an instance of
    3. // whatever class it is declared in.
    4. public Foo create() {
    5. ...
    6. }
    7. }
    8. public class Bar extends Foo {
    9. // Actually creates a Bar.
    10. public Bar create() {
    11. ...
    12. }
    13. }

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

    1. public class Baz extends Bar {
    2. // Actually creates a Baz.
    3. public Foo create() {
    4. ...
    5. }
    6. }

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