Java 实现两个具有相同方法签名但返回类型不同的接口

Java 实现两个具有相同方法签名但返回类型不同的接口,java,oop,inheritance,interface,Java,Oop,Inheritance,Interface,假设我有两个接口: public interface InterfaceA { public int getStuff(); } public interface InterfaceB { public String getStuff(); } public class Testing implements InterfaceA, InterfaceB{ public String getStuff() { return "one"; } } 现在我有了一个实现这两个接

假设我有两个接口:

public interface InterfaceA {
 public int getStuff();

}

public interface InterfaceB {
 public String getStuff();

}
public class Testing implements InterfaceA, InterfaceB{
 public String getStuff() {
   return "one";
 }

}
现在我有了一个实现这两个接口的类:

public interface InterfaceA {
 public int getStuff();

}

public interface InterfaceB {
 public String getStuff();

}
public class Testing implements InterfaceA, InterfaceB{
 public String getStuff() {
   return "one";
 }

}
我的问题是,这会编译吗?如果它编译了,它会运行吗

编辑:

我确实在我的计算机上尝试过这个方法,发现它已经编译并运行了,但是当调用getStuff()时,程序以

未解决的编译问题


您知道Java在较低级别上做了什么吗?

您不能这样做,它也不会编译。

直接取自:

不可能声明一个名为getNumberOfScales的方法 签名和返回类型与 接口Fish和接口StringBass中声明的方法, 因为一个类不能有多个具有相同签名的方法 和不同的原始返回类型(§8.4)。所以是 一个类不可能同时实现Fish和Fish接口 接口弦低音

因此,您不能这样做,它也不会编译。在您的情况下,它会抱怨该方法试图使用不兼容的类型,因为两个方法具有相同的名称,但返回类型不同

未解决的编译问题

只有当您试图运行带有编译器错误的程序时,才会发生此异常

不编译的原因是,您没有完全实现所有接口方法。您只对
InterfaceB
方法执行了操作,而不是
InterfaceA
方法

注意:如果在IDE中没有看到ant错误,请重新启动或清理项目一次

interface I1 { void f(); }
interface I2 { int f(int i); }
interface I3 { int f(); }
class C { public int f() { return 1; } }

class C2 implements I1, I2 {
  public void f() {}
  public int f(int i) { return 1; } // overloaded
}

class C3 extends C implements I2 {
  public int f(int i) { return 1; } // overloaded
}

class C4 extends C implements I3 {
  // Identical, no problem:
  public int f() { return 1; }
}
之所以会出现这种困难,是因为重写、实现和重载令人不快地混合在一起,而重载方法只能根据返回类型而有所不同

现在试试这个:-
接口I4扩展了I1,I3{}

您将得到的错误:-接口I3和I1不兼容;两者都定义了f(),但返回类型不同

在要组合的不同接口中使用相同的方法名通常也会导致代码可读性的混乱。努力避免它

参见链接

这会编译吗?如果它编译了,它会运行吗

您可能会感到惊讶:它不会编译,但如果编译了,它就会运行

Java编译器要求您实现在类上声明的接口中声明的所有方法。在您的情况下,需要同时实现这两个
public int getStuff()
公共字符串getStuff()。如果没有,编译器将显示一个错误:

测试不是抽象的,并且不会覆盖InterfaceA中的抽象方法getStuff() 测试中的getStuff()无法在InterfaceA中实现getStuff()

现在,对于Java虚拟机,在类中同时使用这两种方法是非常好的。但是,Java编译器不允许:

方法getStuff()已在类测试中定义

因此,您的代码不会以这种或那种方式编译


你知道Java在较低层次上做了什么吗

让我们回到JVM本身,做一些邪恶的事情。看看这个节目:

我们声明了一个类测试,它声明了两个具有相同名称和参数的方法,唯一的区别是返回类型。它实现了两个接口,并有一个
公共静态void main(String[]args)
方法,该方法调用两个方法(
getStuff()
getStuff()
)并显示结果。这个程序实际上可以运行,虚拟机不会抱怨任何事情

它为什么有效?定义方法的描述符,如下所示:

MethodDescriptor:
    ( ParameterDescriptor* ) ReturnDescriptor
public class Testing implements InterfaceA, InterfaceB {
    @Override
    public String getStuff() {
        throw new Error("Unresolved compilation problem: \n"+
         "\tThe return type is incompatible with InterfaceA.getStuff()\n";
    }
}
此描述符(除了类和方法名之外)用于在运行时解析方法。由于它包含
ReturnDescriptor
,因此可以使用相同名称的多个方法

编译器呢?编译器不使用方法的描述符来识别方法,而是使用方法的签名。该文件包含以下内容:

如果两个方法具有相同的名称和名称,则它们具有相同的签名 参数类型

两个方法或构造函数声明M和N具有相同的参数 如果满足以下所有条件,则输入:

  • 它们具有相同数量的形式参数(可能为零)

  • 它们具有相同数量的类型参数(可能为零)

  • 设A1,…,An为M的类型参数,B1,…,Bn为N的类型参数。将N的类型中每次出现的Bi重命名为Ai后,相应类型变量的边界相同,M和N的形式参数类型相同

因此,在源代码中无法通过返回类型来区分方法,编译器禁止声明只在返回类型上不同的方法。

未解决的编译问题是Eclipse编译器特性(ECJ)。即使代码中有相当严重的错误,此编译器也可以生成已编译的文件。只要不运行错误的代码,就不会有问题。在您的例子中,由于
getStuff()
方法中存在问题,Eclipse将创建如下所示的方法:

MethodDescriptor:
    ( ParameterDescriptor* ) ReturnDescriptor
public class Testing implements InterfaceA, InterfaceB {
    @Override
    public String getStuff() {
        throw new Error("Unresolved compilation problem: \n"+
         "\tThe return type is incompatible with InterfaceA.getStuff()\n";
    }
}
所以,只要你不启动这个方法,你就可以了。一旦启动它,就会抛出一个错误

如果使用javac编译,它根本不会创建.class文件。所以回到您最初的问题,不,Java语言不允许这样做(Java字节码却允许这样做)

如果您尝试调用根本不存在的
InterfaceA
方法(在Eclipse编译之后),将会发生的情况也非常有趣:

运行时,您将看到:

Hello
Exception in thread "main" java.lang.AbstractMethodError: compileTest.Testing.getStuff()I
    at compileTest.Testing.main(Testing.java:12)
也就是说:当您试图调用缺少im的抽象方法时,JVM抛出一个
AbstractMethodError