英文:
Subtyping in Java results in unexpected output
问题
我试图理解Java中的子类型和继承是如何工作的。最近,我遇到了一个让我感到困惑的情况。我有以下包含三个类的代码:
class Super{
String i = "超类的变量";
void m() {
System.out.println(i);
}
}
class Sub extends Super{
String i = "子类的变量";
}
public class Main {
public static void main(String[] args) {
Sub sub = new Sub();
sub.m(); //输出 超类的变量
}
}
我对m()方法的输出感到困惑。我只是不明白为什么在Sub的实例上调用的方法会使用超类的变量。我猜这与变量的隐藏有关,但我无法详细理解其中发生了什么。如果我像下面这样做:
Super sup = new Sub();
sup.m();
那么我理解为什么它会输出超类的变量。这是因为变量在静态方式上绑定,这是我所知道的变量隐藏。但我真的不明白我的示例中发生了什么。
当我取消Sub中的注释时,发生的事情让我更加困惑。我理解方法现在被覆盖了,但我不明白为什么方法现在使用子类的变量,尽管它与留下注释时继承的代码完全相同。我的意思是,它已经存在了!覆盖一个与原来完全相同的方法如何改变任何内容?
我还尝试在方法内部添加 System.out.println(this.getClass().getName());
来确保方法确实在子类的实例上调用。是的,它是这样调用的。
如果有人能帮助我理解这里发生了什么,我将不胜感激!
英文:
I try to understand how subtyping and inheritance works in Java. Recently I ran into a situation that confuses me. I have got the following Code with three classes:
class Super{
String i = "Variable of superclass";
void m() {
System.out.println(i);
}
}
class Sub extends Super{
String i = "Variable of subclass";
/*@Override
void m() {
System.out.println(i);
}*/
}
public class Main {
public static void main(String[] args) {
Sub sub = new Sub();
sub.m(); //Prints Variable of superclass
}
}
I am so confused with the output of m(). I just don't understand why the method, which is called on an instance of Sub, uses the variable of the superclass. I guess it has to do with variable hiding / shadowing but I can't figure out what is going on there in detail. If I do something like
Super sup = new Sub();
sup.m();
then I understand, why it prints the variable of the superclass. That's because variables are bound in a static way which I know as variable hiding / shadowing. But I really don't get, what happens in my example.
I was even more confused about what happened when I removed the comment in Sub. I understand that the method is now overridden but I don't get why the method now uses the variable of the subclass even though it's exactly the same code that would have been inherited if I left the comment. I mean, it was already there! How does overriding a method with the exact same code change anything?
I also tried adding System.out.println(this.getClass.getName());
inside the method to make sure that the method is really called on an instance of sub. And yes, it is.
If anyone could help me out understanding, what's going on here, I would be very thankful!
答案1
得分: 3
方法可以被覆盖,但字段不能。Sub
没有定义 m
方法,所以 sub.m();
调用了 Super
中的 m
方法。在该方法的作用域内,只有在 Super
中定义的 i
字段是可见的。
在 Sub
中定义的 i
字段是一个不同的字段,它遮蔽了同名的超类字段。如果您改为在 Super
和 Sub
中都定义一个 getI()
方法,在 Super
中调用 getI()
(当底层实例的类型是 Sub
时)将使用在 Sub
中定义的方法。
英文:
Methods can be overriden, but fields cannot. Sub
does not define a m
method, so sub.m();
calls the m
method from Super
. In the scope of that method, only the i
field defined in Super
itself is visible.
The i
field defined in Sub
is a distinct field that shadows the field from the superclass of the same name. If you had instead defined a getI()
method in both Super
and Sub
, calling getI()
in Super
(when the underlying instance is of type Sub
) would use the method defined in Sub
.
答案2
得分: 1
当您在子类(在本例中为Sub
)中声明与超类(在本例中为Super
)中已存在的相同名称的变量时,这被称为变量隐藏。
这意味着子类中声明的变量与超类中声明的变量不同,子类不是覆盖超类中的变量,而是创建了一个具有相同名称的新变量。
您可以覆盖m()
方法(就像在您的示例代码中一样),但也可以只是使用构造函数更改值:
超类
class Super {
// 使用final确保值始终设置
final String i;
// 默认构造函数
Super() {
this("超类的变量");
}
// 用于重写
Super(String i) {
this.i = i;
}
void m() {
System.out.println(i);
}
}
子类
class Sub extends Super {
Sub() {
super("子类的变量");
}
}
查看这些链接:
英文:
When you declare a variable with the same name in a subclass (in this case, Sub
) that already exists in the superclass (in this case, Super
), it's what we call variable hiding.
This means that the variable declared in the subclass is different from the variable declared in the superclass, and the subclass is not overriding the variable from the superclass but rather creating a new variable with the same name.
You can override the m()
method (as it is in your example code), but you can also just change the value using constructors:
Super Class
class Super {
// Final to make sure value is always set
final String i;
// Default constructor
Super() {
this("Variable of superclass");
}
// Use to overwrite
Super(String i) {
this.i = i;
}
void m() {
System.out.println(i);
}
}
Sub Class
class Sub extends Super {
Sub() {
super("Variable of subclass");
}
}
Check this out:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论