在Java中的子类型化导致意外的输出。

huangapple go评论131阅读模式
英文:

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 字段是一个不同的字段,它遮蔽了同名的超类字段。如果您改为在 SuperSub 中都定义一个 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:

huangapple
  • 本文由 发表于 2023年8月5日 02:00:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76838232.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定