英文:
Overloading an Overridden method in Java
问题
我有一个实现了接口A的类B,如下所示:
public interface A {
Map<String, Object> doStuff(Integer param1, Integer Param2);
}
public class B implements A {
public Map<String, Object> doStuff(Integer param1, Integer param2) {
return doStuff(param1, param2, newParam3);
}
public Map<String, Object> doStuff(Integer param1, Integer Param2, boolean param3) {
if (param3)
doSomething;
return doneStuff;
}
}
但是当我尝试访问重载但未覆盖的方法时,我会得到编译时错误。这不应该是完全没问题的吗?
英文:
I have a class B that implements interface A such that:
public interface A {
Map<String, Object> doStuff(Integer param1, Integer Param2);
}
public class B implements A {
public Map<String, Object> doStuff(Integer param1, Integer param2) {
return doStuff(param1, param2, newParam3);
}
public Map<String, Object> doStuff(Integer param1, Integer Param2, boolean param3) {
if (param3)
doSomething;
return doneStuff;
}
}
But when I try to access the overloaded but not overridden method I get compile time errors. Shouldn't this be perfectly fine?
答案1
得分: 1
在Java方法调用中,有两个完全分离的步骤。
步骤1是:将方法调用(在您的源代码中)转换为完全限定的方法标识。这完全在编译时发生。
步骤2是:既然我们有了完全限定的方法标识,通过动态查找在对象的实际实例类型(x.getClass()
)开始,并沿着继承层次向上查找实现,找到该标识的实际实现。这完全在运行时发生。
完全限定的方法标识包括包、类、方法名称、参数类型和返回类型。
因此,在您的B类中,有2个不同的方法标识。不同的方法标识代表不同的方法;名称相同的事实是无关紧要的。
换句话说,这个:
A x = new B();
x.doStuff(param1, param2);
被存储为对方法 com.foo.pkg.A::doStuff(Ljava/lang/Integer;L/java/lang/Integer;)Ljava/util/Map;
的INVOKEVIRTUAL/INVOKEINTERFACE调用。这实际上在类文件中;查看一下javap
的输出,或者用十六进制编辑器打开那个文件,您会在那里找到这个字符串,至少从doStuff
开始。
然后只有在运行时,INVOKEINTERFACE com.foo.pkg.A doStuff(Ljava/lang/Integer;L/java/lang/Integer;)Ljava/util/Map
字节码指令才会检查您的x
变量指向的对象的类型,确定它是com.foo.pkg.B
的实例,然后实际上调用B类对此方法的实现来完成工作。
您的第二个doStuff方法的方法标识是 B doStuff(Ljava/lang/Integer;Ljava/lang/Integer;Z)Ljava/util/Map;
- 请注意这完全是另一个方法(这个方法中有一个Z)。
因此,这个:
A x = new B();
x.doStuff(1, 2, true);
会导致编译错误:A接口根本没有这个方法。
英文:
There are two entirely separated steps in java method invocation.
Step 1 is: Turn the method invocation (in your source code) into the fully qualified method ID. This occurs entirely at compile time.
Step 2 is: Now that we have a fully qualified method ID, find the actual implementation for this ID by doing a dynamic lookup, starting at the object's actual instance type (x.getClass()
), and going upwards in the hierarchy until you find an implementation. This occurs entirely at runtime.
A fully qualified method ID includes the package, class, method name, parameter types and return type.
So, in your class B you have 2 different method IDs. And different method IDs are different methods; the fact that the name is the same is irrelevant.
In other words, this:
A x = new B();
x.doStuff(param1, param2);
is stored as an INVOKEVIRTUAL/INVOKEINTERFACE to method com.foo.pkg.A::doStuff(Ljava/lang/Integer;L/java/lang/Integer;)Ljava/util/Map;
. That's literally in the class file; have a look at the output of javap
or actually go open that thing with a hex editor and you'll find that string in there, at least, starting at doStuff
.
Then at runtime only, that 'INVOKEINTERFACE com.foo.pkg.A doStuff(Ljava/lang/Integer;L/java/lang/Integer;)Ljava/util/Mapbytecode instruction ends up checking the type of the object that your
xvariable is pointing to, figures out it's an instance of
com.foo.pkg.B`, and will then actually call B's implementation of this method to do the job.
Your second doStuff method has method ID B doStuff(Ljava/lang/Integer;Ljava/lang/Integer;Z)Ljava/util/Map;
- note how this is just a completely different method (this one has a Z in it).
Thus, this:
A x = new B();
x.doStuff(1, 2, true);
is a compilation error: The A interface doesn't have that method at all.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论