【问题标题】:Casting in Java - what does (Parent) this mean?在 Java 中进行铸造 - (Parent) 这是什么意思?
【发布时间】:2020-10-01 11:50:36
【问题描述】:
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);,它们都有相同的输出。这里到底发生了什么?

【问题讨论】:

  • System.out.println(this);System.out.println((A)this); 有效地调用对象实例的toString。方法相互覆盖,因此输出将始终相同。
  • @Robert 好的,但 println 在这些示例中没有 this(A)this 作为参数。
  • (A) this 只是将this 转换为A。所以((A) this).a 将首先将this 转换为A,然后按照类A 中的定义查找a。它应该打印 100。
  • 字段“覆盖”(有真正的字段覆盖是否更隐藏)与方法覆盖完全不同:stackoverflow.com/questions/9414990/…
  • "我也试过 System.out.println(this); 和 System.out.println((A)this); ..." 方法有动态调度。字段不

标签: java class inheritance this


【解决方案1】:

在 java 编程语言中,我们有类。当我们编写 java 代码时,我们会创建这些类的实例,例如:

Object o = new Object();

Object 是一个类。编写new Object() 会创建该类的一个实例。上面的代码声明了一个变量o,并为它分配了一个Object类的实例[a reference to]。

在 java 编程语言的术语中,我们说变量o 具有类型 Object

在您问题的代码中,分配了C 类实例的变量实际上具有三种类型。

  • 类型为C
  • 它的类型为B,因为BC 的超类。
  • 它具有A 类型,因为它也间接扩展了A 类。

在您问题中的代码上下文中,this 是一个特殊变量,其类型为 C。写(A) this 是告诉java 与变量this 相关,就好像它的类型是A

A 类无法访问其子类。因此它只知道它的类成员a。因此,当您编写这行代码时...

((A) this).a

您正在访问类A 的成员。

【讨论】:

    【解决方案2】:
    • System.out.println(a);
      a 是您的 C 类的 show 方法中的一个 → a = 0

    • System.out.println(super.a);
      aC的超类,即Ba = 80

    • System.out.println(((A) this).a);
      首先,将C 实例(this)转换为A,然后调用a,它是A 类的成员→ a = 100

    【讨论】:

      【解决方案3】:

      还有一点需要考虑:方法总是采用更专业的方法(除非使用 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 不会对方法产生太大影响,因为它会影响字段。

      【讨论】:

        【解决方案4】:

        如果您编写类似obj.aobj.getA()someMethod(obj) 的内容,Java 必须以某种方式根据obj 的类型或类找到要使用的实际字段或方法。涉及两种不同的调度机制(加上特殊的构造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-dispatch 表达式,这意味着此类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 的子类的对象上调用了show(),例如DD 有自己的 getA() 方法,将使用该方法。

        • super.getA()super-dispatch 的一个例子,它会在当前类的类层次结构中调用getA() 方法,例如B.

        【讨论】:

          【解决方案5】:
          System.out.println(this);
          

          System.out.println((A)this)
          

          这两个使用toString()方法打印对C类的对象引用。

          System.out.println(((A)this).a);
          

          这是向上转换,子对象到父对象。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-08-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多