假设我有两个界面:
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在较低的层面上做了什么?
答案 0 :(得分:4)
这会编译吗?如果它确实编译,它会运行吗?
你可能会感到惊讶:它不会编译,但如果确实如此,它就会运行。
Java编译器要求您实现在类中声明的接口中声明的所有方法。在您的情况下,您需要实施 public int getStuff();
和public String getStuff();
。如果不这样做,编译器将显示错误:
测试不是抽象的,并且不会覆盖InterfaceA中的抽象方法getStuff() Testing中的getStuff()无法在InterfaceA中实现getStuff()
现在,对于Java虚拟机,在您的类中同时使用这两种方法是完全可以的。但是,Java编译器不允许它:
方法getStuff()已在类Testing
中定义
所以你的代码不会以某种方式编译。
这里有什么想法,Java在较低的层面上做了什么?
让我们回到JVM本身并做一些邪恶的事情。看看这个jasmin计划:
.class public Testing
.super java/lang/Object
.implements InterfaceA
.implements InterfaceB
.method public static getStuff()I
ldc 666
ireturn
.end method
.method public static getStuff()Ljava/lang/String;
ldc "Evil stuff"
areturn
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 10
getstatic java/lang/System/out Ljava/io/PrintStream;
dup
invokestatic Testing/getStuff()I
invokevirtual java/io/PrintStream/println(I)V
invokestatic Testing/getStuff()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
return
.end method
我们声明一个类Testing,它声明了两个具有相同名称和参数的方法,唯一的区别是返回类型。它实现了两个接口,并且具有public static void main(String[] args)
方法,该方法调用两种方法(getStuff()
和getStuff()
)并显示结果。这个程序实际上是可行的,虚拟机不会抱怨任何事情。
为什么会这样? JVM specification定义了这样的方法的描述符:
MethodDescriptor:
( ParameterDescriptor* ) ReturnDescriptor
此描述符(除了类和方法名称之外)用于在运行时解析方法。由于它包含ReturnDescriptor
,因此可以使用多个具有相同名称的方法。
编译器怎么样?编译器不使用描述符识别方法,而是使用签名。 Java language specification包含以下内容:
如果两个方法具有相同的名称,则它们具有相同的签名 参数类型。
两个方法或构造函数声明M和N具有相同的参数 如果满足以下所有条件,则输入类型:
它们具有相同数量的形式参数(可能为零)
它们具有相同数量的类型参数(可能为零)
设A1,...,An为M的类型参数,让B1,...,Bn为N的类型参数。将N的类型中每次出现的Bi重命名为Ai后,相应类型变量的边界是相同的,M和N的形式参数类型是相同的。
因此,无法通过源代码中的返回类型来区分方法,并且编译器禁止声明仅在返回类型方面不同的方法。
答案 1 :(得分:2)
未解决的编译问题
仅当您尝试运行带有编译器错误的程序时才会发生此异常。
并且没有编译的原因,你没有完全实现所有的接口方法。您只使用InterfaceB
方法,而不是InterfaceA
方法。
注意:如果您在IDE中没有看到ant错误,请重启或清理一次项目。
答案 2 :(得分:0)
“未解决的编译问题”特别是Eclipse编译器功能(ECJ)。即使您的代码中存在严重错误,此编译器也可以生成已编译的文件。只要你不运行错误的代码,你就没事了。在您遇到getStuff()
方法问题的情况下,Eclipse会创建一个如下所示的方法:
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编译之后)会发生什么,这也很有趣:
public class Testing implements InterfaceA, InterfaceB {
@Override
public String getStuff() { // compilation error here, but class is still generated
return "one";
}
public static void main(String[] args) {
System.out.println("Hello");
InterfaceA a = new Testing();
int i = a.getStuff();
System.out.println(i);
}
}
当你运行它时,你会看到:
Hello
Exception in thread "main" java.lang.AbstractMethodError: compileTest.Testing.getStuff()I
at compileTest.Testing.main(Testing.java:12)
即:当您尝试调用缺少实现的抽象方法时,JVM会抛出AbstractMethodError
。从JVM的角度来看,String getStuff()
和int getStuff()
是两种完全不同的方法,第二种方法在我们的课堂中不存在。