在Java中的类型转换 – (Parent) this 代表什么?

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

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 thisor 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,因为 BC 的超类(父类)。
  • 它有类型 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 since B is the superclass of C.
  • It has type A because it indirectly extends class A 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的父类Ba = 80

  • System.out.println(((A) this).a);<br/>首先,你将C实例(this)转换为A,然后调用属于A类的aa = 100

英文:
  • System.out.println(a);<br/>a is the one from the show method of your C class → a = 0

  • System.out.println(super.a);<br/>a is the one from the super-class of C, which is Ba = 80

  • System.out.println(((A) this).a);<br/>First, you cast your C instance (this) into A, then you call a which is a member of the A 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.aobj.getA()或者someMethod(obj)的代码,在Java中,根据obj的类型或类,Java需要找到要使用的实际字段或方法。涉及到两种不同的派发机制(还有特殊的构造super)。

  • 动态派发(多态,重写):在调用对象的实例方法时使用,比如obj.getA()。然后会检查obj的运行时类,如果这个类包含getA()方法,就会使用它。否则,会检查直接父类是否包含getA()方法,依此类推,直到Object类。

  • 静态派发:在obj.asomeMethod(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派发表达式,意味着忽略来自类Ca字段,而使用继承层次中更高的那个字段(在我们的情况下是来自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 the obj is examined, and if this class contains a getA() method, this is used. Otherwise, the direct parent class is examined for a getA() method, and so on up to the Object class.

  • Static dispatch: In cases like obj.a or someMethod(obj), the runtime class of obj doesn't matter. Involved is only the compiler, and from his knowledge of obj's type, he decides which field or method to use.

  • super dispatch: If you write super.getA() or super.a, your getA() method or a 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 variable a. 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 of this. And that's always the class where this code has been written. In your case, it's class C, so the field a from class C is used, the one being 10.

  • super.a is a super-dispatch expression, meaning that the a field from this class C is ignored and the next higher one taken (the one from B, in our case).

  • ((A) this).a is static dispatch, but the (A) casting has a significant effect. The expression before the dot originally comes from this, being of type C, but the (A) cast tells the compiler to believe it were of type A. This is okay, as every C also is an A, by inheritance. But now, static dispatch sees something of type A in front of the dot, and dispatches to the a field from the A class, and no longer from C.

  • 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 the C class. But if show() was called on an object of a subclass of C, e.g. D, and D had its own getA() method, that one would be used.

  • super.getA() is a case of super-dispatch, it will call the getA() 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.

huangapple
  • 本文由 发表于 2020年10月1日 19:50:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/64154799.html
匿名

发表评论

匿名网友

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

确定