我可以将返回类型更改为严格的子类型并保留二进制兼容性吗?

时间:2017-11-24 15:46:58

标签: java binary-compatibility

如果我有一些课程:

import java.util.Date;
public final class Foo {
    private Date date;
    public Date getDate(){ return date; }
}

如果我将其编译为二进制文件并且其他人已经构建了代码,那么我可以在不破坏二进制兼容性的情况下执行此操作吗?

import java.sql.Date;
public final class Foo {
    private Date date;
    public Date getDate(){ return date; }
}

请注意java.sql.Datejava.util.Date的子类。

对我而言,似乎很明显,如果我没有宣布该类最终,那么我会破坏源代码兼容性(即,某人之前可能已经针对我的库编译了Foo的子类,它覆盖了getDate }方法返回java.util.Date;该代码不再编译对我的最新版本)。但是破坏源兼容性是否意味着二进制兼容性也被破坏了? (不能用其他语言保存,例如scala)

3 个答案:

答案 0 :(得分:0)

您拥有的针对java.util.Date 的任何代码应该继续针对其子类正常运行 - 假设java.sql.Date确实破坏了java.util.Date的任何行为。

具体而言,java.sql.Date会对java.lang.IllegalArgumentException的几个重写弃用方法(例如java.util.Date)抛出getHours()。您应该厌倦的一种方法是toInstant(),这在现代Java应用程序中可能非常有用,但在java.lang.IllegalArgumentException中只会引发java.sql.Date

答案 1 :(得分:0)

我认为最好的方法就是亲自测试一下。以下是官方documentation的一些摘录:

  

<强> 15年4月13日。方法结果类型

     

更改方法的结果类型,或用结果类型替换   void,或用结果类型替换void,具有组合效果   删除旧方法并使用新结果添加新方法   类型或新的结果(见§13.4.12)。

  

<强> 13.4.12。方法和构造函数声明

     

从类中删除方法或构造函数可能会破坏兼容性   使用引用此方法的任何预先存在的二进制文件   构造函数;这样的引用可能会引发NoSuchMethodError   从预先存在的二进制文件链接。只有在发生此类错误时才会发生   没有具有匹配签名和返回类型的方法在a中声明   超类。

我读这个的方式是,如果要更改返回类型,无论它是旧类型的子类型,都意味着您要删除旧方法并添加新方法并根据 13.4.12 它可能会破坏兼容性 使用引用此方法或构造函数的任何预先存在的二进制文件。

答案 2 :(得分:0)

我对此进行了测试。

不幸的是,当方法参数更改为原始类型的子类型时,会发生NoSuchMethodError异常。因此,进行此更改与二进制向后不兼容。

测试设置:

test
├── c
├── c_int
│   └── C.java
├── c_num
│   └── C.java
└── Test.java

test/c_num/C.java

package test.c;

public class C {
    public Number f() {
        System.out.println("f Number");
        return 0.0;
    }
}

test/c_int/C.java

package test.c;

public class C {
    public Integer f() {
        System.out.println("f Integer");
        return 1;
    }
}

test/Test.java

package test;

import test.c.C;

public class Test {
    public static void main(String[] args) throws Exception {
        C b = new C();
        Number n = b.f();
        System.out.println(n);
    }
}

测试:

$ javac test/c_int/C.java
$ javac test/c_num/C.java
$ cp test/c_num/C.class test/c/
$ javac test/Test.java
$ java test.Test
f Number
0.0
$ cp test/c_int/C.class test/c/
$ java test.Test
Exception in thread "main" java.lang.NoSuchMethodError: 'java.lang.Number test.c.C.f()'
    at test.Test.main(Test.java:8)

我们还可以在字节码中看到对f的方法调用包含方法的返回类型:

$ javap -c test/Test.class
Compiled from "Test.java"
public class test.Test {
  public static void main(java.lang.String[]);
    Code:
       ...
       9: invokevirtual #4                  // Method test/c/C.f:()Ljava/lang/Number;
       ...
}