简体   繁体   English

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

[英]Can I change a return type to be a strict subtype and retain binary compatibility?

If I have some class:如果我有一些课程:

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

If I have compiled this as a binary and other people have built code against it, can I subsequently do this without breaking binary compatibility?如果我将它编译为二进制文件,而其他人已经针对它构建了代码,那么我是否可以在不破坏二进制兼容性的情况下执行此操作?

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

Note that java.sql.Date is a subclass of java.util.Date .请注意, java.sql.Datejava.util.Date的子类。

It seems obvious to me that, had I not declared the class final, then I would have broken source compatibility (ie someone could previously have compiled against my library a subclass of Foo which overrides the getDate method to return a java.util.Date ; that code would no longer compile against my latest version).对我来说很明显,如果我没有将类声明为 final,那么我就会破坏源兼容性(即,之前有人可能已经针对我的库编译了Foo的子类,它覆盖了getDate方法以返回java.util.Date ;该代码将不再针对我的最新版本进行编译)。 But does breaking source compatibility imply that binary compatibility is broken too?但是破坏源代码兼容性是否意味着二进制兼容性也被破坏了? (that doesn't hold in other languages, such as scala) (这在其他语言中不成立,例如 scala)

I tested this.我测试了这个。

Unfortunately there is a NoSuchMethodError exception at the point of the call when a method's return type is changed to a subtype of the original type.不幸的是,当方法的返回类型更改为原始类型的子类型时,在调用点会出现NoSuchMethodError异常。

Making this change is thus not binary backwards compatible.因此,进行此更改不是二进制向后兼容的。

This is too bad...这太糟糕了...

Test setup测试设置

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

test/c_num/C.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 : test/c_int/C.java

package test.c;

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

test/Test.java : 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);
    }
}

Test测试

Compile the two C classes with different return type:用不同的返回类型编译两个C类:

$ javac test/c_int/C.java
$ javac test/c_num/C.java

Compile Test against C with return type Number :使用返回类型NumberC进行编译Test

$ cp test/c_num/C.class test/c/
$ javac test/Test.java

Run Test against C with return type Number :使用返回类型NumberC运行Test

$ java test.Test
f Number
0.0

Run Test against C with return type Integer , without recompiling Test :使用返回类型IntegerC运行Test ,无需重新编译Test

$ 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)

Bytecode字节码

We can also see in the byte code that the method call to f contains the return type of the method:我们还可以在字节码中看到对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;
       ...
}

Eclipse document日食文档

The Eclipse foundation maintain a document called Evolving Java-based APIs . Eclipse 基金会维护一个名为Evolving Java-based APIs的文档。

The document states this about binary compatibility:该文档说明了有关二进制兼容性的内容:

Evolving API classes - API methods and constructors
....
Change result type (including void)     -   Breaks compatibility

This does not make an exception about changes that only narrow the return type.这对仅缩小返回类型的更改也不例外。 All changes to the return type breaks binary compatibility.对返回类型的所有更改都会破坏二进制兼容性。

I think the best way is to just test it yourself.我认为最好的方法是自己测试一下。 Here is some snippet from official documentation :以下是官方文档的一些片段:

13.4.15. 13.4.15. Method Result Type方法结果类型

Changing the result type of a method, or replacing a result type with void, or replacing void with a result type, has the combined effect of deleting the old method and adding a new method with the new result type or newly void result (see §13.4.12).更改方法的结果类型,或用 void 替换结果类型,或用结果类型替换 void,具有删除旧方法并添加具有新结果类型或新的 void 结果的新方法的综合效果(参见 § 13.4.12)。

and

13.4.12. 13.4.12. Method and Constructor Declarations方法和构造函数声明

Deleting a method or constructor from a class may break compatibility with any pre-existing binary that referenced this method or constructor;从类中删除方法或构造函数可能会破坏与引用此方法或构造函数的任何预先存在的二进制文件的兼容性; a NoSuchMethodError may be thrown when such a reference from a pre-existing binary is linked.当链接来自预先存在的二进制文件的此类引用时,可能会抛出 NoSuchMethodError 。 Such an error will occur only if no method with a matching signature and return type is declared in a superclass.只有在超类中没有声明具有匹配签名和返回类型的方法时,才会发生这样的错误。

The way I read this is that if you are changing return type, no matter if it is a subtype of old type, means that you are deleting the old method and adding a new method and according to 13.4.12 it can break compatibility with any pre-existing binary that referenced this method or constructor.我读这个的方式是,如果您更改返回类型,无论它是否是旧类型的子类型,都意味着您正在删除旧方法并添加新方法,并且根据13.4.12它可以破坏与任何引用此方法或构造函数的预先存在的二进制文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 子类型和协变返回类型 - subtype and covariant return type 覆盖和返回类型兼容性 - Overriding and return type compatibility 将返回类型从Integer更改为int,而不会破坏向后兼容性 - Change return type from Integer to int without breaking backwards compatibility “……的返回类型不是被重写成员的返回类型的子类型”吗? - “Return type of … is not a subtype of the return type of the overridden member”? 如何在类定义的通用类型中捕获子类型? - How can I capture a subtype within a generic type in a class definition? 我可以在Java中将扩展类中的可变类型强制为子类型吗? - Can I force the type of a variabile in a extended class to a subtype in Java? Java二进制兼容性 - 使用invokevirtual语义提出的协变返回类型解决方案的RFC - Java binary compatibility - RFC on proposed solution to covariant return type using invokevirtual semantics 检查 ExecutableElement 的返回类型是 Collection 的子类型 - Check return type of ExecutableElement is subtype of Collection 如何在方向更改时保留幻灯片的特定文本? - How can I retain the particular text of a slide on orientation change? 二进制搜索树更改插入方法返回类型 - Binary search tree change insert method return type
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM