英文:
Casting in Java - what does (Parent) this mean?
问题
class A {
public int a = 100;
}
class B extends A {
public int a = 80;
}
class C extends B {
public int a = 10;
public void show() {
int a = 0;
System.out.println(a);
System.out.println(super.a);
System.out.println(((A) this).a);
}
}
((A) this).a
在这一行 System.out.println(((A) this).a);
中的作用是什么?
它是对 this
进行上转型还是下转型,还是发生了其他情况?
我还尝试了 System.out.println(this);
和 System.out.println((A)this);
,它们的输出都相同。这里究竟发生了什么?
英文:
class A {
public int a = 100;
}
class B extends A {
public int a = 80;
}
class C extends B {
public int a = 10;
public void show() {
int a = 0;
System.out.println(a);
System.out.println(super.a);
System.out.println(((A) this).a);
}
}
What does ((A) this).a
in the line System.out.println(((A) this).a);
do?
Is it upcasting/downcasting this
or is something else happening here?
I also tried System.out.println(this);
and System.out.println((A)this);
and they both have the same output. What exactly is happening here?
答案1
得分: 2
在Java编程语言中,我们有类(classes)。当我们编写Java代码时,我们创建这些类的实例,例如:
Object o = new Object();
Object
是一个类(class)。写下 new Object()
创建了该类的一个实例。上面的代码声明了一个变量 o
,并将其赋值为指向 Object
类的一个实例的引用。
在Java编程语言的术语中,我们说变量 o
的类型是 Object
。
在你的问题的代码中,一个被赋值为 C
类的实例的变量,实际上有三种类型。
- 它有类型
C
。 - 它有类型
B
,因为B
是C
的超类(父类)。 - 它有类型
A
,因为它也间接地继承了类A
。
在你的问题的代码上下文中,this
是一个特殊的变量,其类型为 C
。写下 (A) this
是在告诉Java将变量 this
视为其类型为 A
。
类 A
无法访问其子类。因此它只知道它的类成员 a
。因此,当你写下这行代码...
((A) this).a
你在访问的是 A
类的成员。
英文:
In the java programming language, we have classes. When we write java code, we create instances of those classes, for example:
Object o = new Object();
Object
is a class. Writing new Object()
creates an instance of that class. The above code declares a variable o
and assigns it [a reference to] an instance of class Object
.
In the terminology of the java programming language, we say that variable o
has type Object
.
In the code in your question, a variable that is assigned an instance of class C
, really has three types.
- It has type
C
. - It has type
B
sinceB
is the superclass ofC
. - It has type
A
because it indirectly extends classA
also.
In the context of the code in your question, this
is a special variable whose type is C
. Writing (A) this
is telling java to relate to the variable this
as if its type is A
.
Class A
cannot access its subclasses. Hence it is only aware of its class member a
. Hence when you write this line of code...
((A) this).a
You are accessing the member of class A
only.
答案2
得分: 1
Sure, here's the translated content:
-
System.out.println(a);
<br/>其中的a
来自于你的C
类的show
方法 → a = 0 -
System.out.println(super.a);
<br/>其中的a
来自于C
的父类B
→ a = 80 -
System.out.println(((A) this).a);
<br/>首先,你将C
实例(this
)转换为A
,然后调用属于A
类的a
→ a = 100
英文:
-
System.out.println(a);
<br/>a
is the one from theshow
method of yourC
class → a = 0 -
System.out.println(super.a);
<br/>a
is the one from the super-class ofC
, which isB
→ a = 80 -
System.out.println(((A) this).a);
<br/>First, you cast yourC
instance (this
) intoA
, then you calla
which is a member of theA
class → a = 100
答案3
得分: 1
还有一些需要考虑的地方:方法始终会采用更专门的那个(除非使用 super 关键字),而字段将直接从所引用的类型中获取(即使存在扩展类)。
例如,如果我在每个类中添加 getA() 方法:
class A {
public int a = 100;
public int getA(){
return a;
}
}
class B extends A {
public int a = 80;
public int getA(){
return a;
}
}
class C extends B {
public int a = 10;
public int getA(){
return a;
}
public void show() {
int a = 0;
System.out.println(a);
System.out.println(super.a);
System.out.println(((A) this).a);
System.out.println(getA());
System.out.println(super.getA());
System.out.println(((A) this).getA());
}
}
class Scratch {
public static void main(String[] args) {
new C().show();
}
}
我得到以下输出:
0
80
100
10
80
10
这意味着在方法的情况下,除了 super.getA()
明确访问超类的情况外,将 C 强制转换为 A 对于方法几乎没有太大影响,因为它影响的是字段。
英文:
There is also something to consider : method will always take the more specialized one (except if super is used), where field will be taken directly from the type referenced (even if there is an extending class).
For example, if I add getA() in each classes :
class A {
public int a = 100;
public int getA(){
return a;
}
}
class B extends A {
public int a = 80;
public int getA(){
return a;
}
}
class C extends B {
public int a = 10;
public int getA(){
return a;
}
public void show() {
int a = 0;
System.out.println(a);
System.out.println(super.a);
System.out.println(((A) this).a);
System.out.println(getA());
System.out.println(super.getA());
System.out.println(((A) this).getA());
}
}
class Scratch {
public static void main(String[] args) {
new C().show();
}
}
I get the following output :
0
80
100
10
80
10
Which means that in the case of the method, except in the case of super.getA()
which explicitly goes to the superclass, casting your C into a A doesn't change much for methods, as it impacts the field.
答案4
得分: 1
如果您编写类似于obj.a
,obj.getA()
或者someMethod(obj)
的代码,在Java中,根据obj
的类型或类,Java需要找到要使用的实际字段或方法。涉及到两种不同的派发机制(还有特殊的构造super
)。
-
动态派发(多态,重写):在调用对象的实例方法时使用,比如
obj.getA()
。然后会检查obj
的运行时类,如果这个类包含getA()
方法,就会使用它。否则,会检查直接父类是否包含getA()
方法,依此类推,直到Object
类。 -
静态派发:在
obj.a
或someMethod(obj)
等情况下,obj的运行时类并不重要。只涉及到编译器,根据他对obj
类型的了解,决定使用哪个字段或方法。 -
super
派发:如果您写了super.getA()
或super.a
,会忽略您的getA()
方法或a
字段,而是使用继承层次结构中的下一个更高的类,该类包含这样的方法或字段。
在您的情况下,有3个字段和一个局部变量,它们都叫做a
。(顺便说一句,在专业代码中有这样的名称冲突是一个非常糟糕的主意。)我们在C
类中声明的show()
方法内部。让我们看一些不同的表达式及其在这里的意义:
-
a
引用局部变量a
。不需要派发,只是局部定义优先于字段。 -
this.a
是一个静态派发表达式,所以编译器对this
的类型是重要的。而这始终是编写此代码的类。在您的情况下,是C
类,因此使用C
类的字段a
,即为10。 -
super.a
是一个super
派发表达式,意味着忽略来自类C
的a
字段,而使用继承层次中更高的那个字段(在我们的情况下是来自B
的字段)。 -
((A) this).a
是静态派发,但(A)
转换有重要影响。点号前面的表达式最初来自this
,类型为C
,但(A)
转换告诉编译器将其视为类型A
。这是可以的,因为每个C
也都是一个A
,通过继承。但是现在,静态派发在点号前面看到了一个类型为A
的东西,并且派发到A
类的a
字段,而不再是C
类的。 -
getA()
,this.getA()
和((A) this).getA()
都是动态派发的示例,都会得到相同的结果。调用的方法将基于此对象的运行时类。这通常是在C
类中定义的方法。但是如果在C
的子类(例如D
)的对象上调用了show()
,并且D
有自己的getA()
方法,那么将使用那个方法。 -
super.getA()
是super
派发的情况,它将调用类层次结构中当前类更高层次的getA()
方法,例如B
类。
英文:
If you write something like obj.a
, obj.getA()
or someMethod(obj)
, Java somehow has to find the actual field or method to be used, based on the type or class of obj
. There are two distinct dispatch mechanisms involved (plus the special construct super
).
-
Dynamic dispatch (polymorphism, overriding): This is used when calling an instance method on the object, as in
obj.getA()
. Then the runtime class of theobj
is examined, and if this class contains agetA()
method, this is used. Otherwise, the direct parent class is examined for agetA()
method, and so on up to theObject
class. -
Static dispatch: In cases like
obj.a
orsomeMethod(obj)
, the runtime class of obj doesn't matter. Involved is only the compiler, and from his knowledge ofobj
's type, he decides which field or method to use. -
super
dispatch: If you writesuper.getA()
orsuper.a
, yourgetA()
method ora
field is ignored, and instead the next-higher class in the hierarchy is used that contains such a method or field.
In your case you have 3 fields plus one local variable, all with the same name a
. (By the way, it's a very bad idea to have such name conflicts in professional code.) We are inside a method show()
declared in the C
class. Let's have a look at some different expressions and what they mean here:
-
a
references the local variablea
. There's no dispatch needed, it's just that local definitions take precedence over fields. -
this.a
is a static-dispatch expression, so it's important what the compiler thinks about the type ofthis
. And that's always the class where this code has been written. In your case, it's classC
, so the fielda
from classC
is used, the one being 10. -
super.a
is asuper
-dispatch expression, meaning that thea
field from this classC
is ignored and the next higher one taken (the one fromB
, in our case). -
((A) this).a
is static dispatch, but the(A)
casting has a significant effect. The expression before the dot originally comes fromthis
, being of typeC
, but the(A)
cast tells the compiler to believe it were of typeA
. This is okay, as everyC
also is anA
, by inheritance. But now, static dispatch sees something of typeA
in front of the dot, and dispatches to thea
field from theA
class, and no longer fromC
. -
getA()
,this.getA()
and((A) this).getA()
are all dynamic-dispatch examples, all giving the same result. The method called will be the one based on the runtime class of this object. This will typically be one defined in theC
class. But ifshow()
was called on an object of a subclass ofC
, e.g.D
, andD
had its owngetA()
method, that one would be used. -
super.getA()
is a case ofsuper
-dispatch, it will call thegetA()
method next higher up in the class hierarchy from the current class, e.g.B
.
答案5
得分: 0
System.out.println(this);
And
System.out.println((A)this)
这两个语句打印类 C 的对象引用,使用了 toString()
方法。
System.out.println(((A)this).a);
这是向上转型,将子类对象转换为父类对象。
英文:
System.out.println(this);
And
System.out.println((A)this)
These two prints the object reference to class C with toString()
method.
System.out.println(((A)this).a);
This is upcasting, child object to parent object.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论