问题描述
我的问题是在以下情况下我无法理解方法解析的工作方式:假设我们有两个包,A
和B
.有两个类,A
放在A
内,B
放在B
内.
My problem is that I can't understand how method resolution works in the following case: Suppose, we have two packages, A
and B
. There are two classes, A
is placed within A
, B
within B
.
A:
package com.eka.IO.a;
import com.eka.IO.b.B;
public class A {
void foo() {
System.out.println("parent");
}
public static void main(String... args) {
B obj = new B();
obj.foo();
}
}
B:
package com.eka.IO.b;
import com.eka.IO.a.A;
public class B extends A {
public void foo() {
System.out.println("child");
}
}
上面的代码显示"child",这完全可以.但是,如果我通过以下方式更改main方法:
The code above prints "child", which is perfectly OK. But if I change the method main the following way:
public static void main(String... args) {
A obj = new B();
obj.foo();
}
代码显示父母",我不明白为什么. (obj
的运行时类型为B
,B
的公共方法为foo
)
the code prints "parent", and I don't understand why. (obj
has runtime type B
, B
has a public method foo
)
接下来,我将foo的公开范围更改为公开,
Next, I change foo's visibility to public,
public class A {
public void foo() {
,并且代码再次打印"child".
and the code prints "child" again.
据我所知,实例方法是使用以下原理在运行时解析的:
As far as I know, instance methods are resolved at runtime, using the following principle:
- JVM检查对象的运行时类.
- JVM寻找运行时类的方法
- 如果找到了方法,则JVM会调用它,否则将移至父运行时类.
在我的示例中,在任何三种情况下,obj
的运行时类始终为B
. B
的方法foo
始终是公共的.为什么在第二种情况下JVM调用A
的方法?
In my example, in any of three cases, runtime class for obj
is always B
. B
's method foo
is always public. Why in the second case JVM calls A
's method?
上:好的答案,但对于我来说仍然有些事情还不清楚.a)由编译器检查一个方法是否覆盖另一个方法. (希望,我是对的).b)在A obj = new B();
的情况下,编译器生成以下代码:
Up:Good answers, but still some things are unclear for me.a) It's the compiler that checks whether a method overrides another method. (Hope, I'm right).b) in case of A obj = new B();
the compiler generates the following code:
INVOKEVIRTUAL com/eka/IO/a/A.foo ()V
b1)如果声明A的foo不带修饰符(包可见性),则JVM调用A的方法.b2)如果将A的foo声明为public,则JVM会调用B的方法.
b1)if A's foo is declared without modifier (package visibility), then JVM calls A's method.b2)if A's foo is declared public, then JVM calls B's method.
不清楚的是为什么在第二种情况下INVOKEVIRTUAL实际上调用B.foo.怎么知道B覆盖了该方法?
The unclear thing is why in the second case INVOKEVIRTUAL actually calls B.foo. How does it know, that B overrides the method?
推荐答案
该过程与您描述的过程略有不同.首先,Java将仅使存在于已声明类中并且在当前作用域中可见的方法可用.这已经在编译时完成了.
The process is slightly different than you described it. First, Java will only make the methods that exist in the declared class and are visible at the current scope available. This is already done at compile time.
在运行时,
- JVM检查对象的运行时类.
- JVM检查对象的运行时类是否重写了声明的类的方法.
- 如果是这样,那就是调用的方法.否则,将调用声明的类的方法.
现在,棘手的部分是它已被覆盖"吗?
Now, the tricky part is "has it been overridden"?
一个类不能覆盖它不可见的方法.它可以用相同的名称和相同的参数声明一个方法,但是该方法不被视为覆盖原始方法.这只是一个新方法,就像在B中定义的任何其他方法一样,但在A中没有定义.
A class can't override a method that is not visible to it. It can declare a method by the same name and with the same arguments, but this method is not considered to be overriding the original method. It's simply a new method, just like any other method that's defined in B but not in A.
如果不是这样,那么您可以在作者认为不应破坏父母的集体合同的地方,因此不允许使用它.
If this was not so, then you could break the parent's class contract at a place where the author thought it should not be broken and therefore did not allow access to it.
因此,由于该类未覆盖该方法,因此只能以与引用B中声明的任何方法相同的方式引用该方法,而该方法不在A中-只能通过B引用.
So since the class did not override the method, you can only reference that method the same way you'd be able to reference any method declared in B that was not in A - only through a B reference.
那么为什么编译器不会阻止您使用父类中已经存在的方法的名称呢?
Why doesn't the compiler prevent you from using names of methods that are already in the parent class, then?
好吧,如果您得到一个包,并且您唯一拥有的信息就是类合同中的内容(如其Javadoc中所写),那么您甚至都不知道该方法的存在.突然之间,您编写了一种据您所知唯一的方法,并且遇到了编译错误.
Well, if you get a package, and the only information you have about it is what's in the classes' contracts, as written in its Javadoc, you won't even know about the existence of that method. All of a sudden, you write a method that, as far as you know, is unique, and you get a compilation error.
没有理由这样做.您不可见的内容不应阻止您自由命名自己的方法.这样就可以了.
There is no reason to do that. What's not visible to you should not prevent you from freely naming your own methods. Thus it is allowed.
但是,如果您希望编译器防止发生此类错误,请在编写假定的方法来覆盖父类的方法时使用@Override
批注.这样,如果您试图覆盖不属于类约定的方法,则编译器会警告您.
But if you want the compiler to prevent you from making mistakes like that, use the @Override
annotation whenever you are writing a method that is supposed to override a parent class's method. This way, the compiler will warn you if you are trying to override a method that is not part of the class's contract.
这篇关于如何使用默认(程序包)可见性范围覆盖方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!