实例方法覆盖与字段隐藏

Instance method overiding vs Field hiding

下面是代码,

package ClassesOverridingHidingAccess;

interface I{
    int x = 0;
}
class T1 implements I{
    int x = 1;
    String s(){
        return "1";
    }
}

class T2 extends T1{
    int x  = 2;
    String s(){
        return "2";
    }
}

class T3 extends T2{
    int x = 3;
    String s(){
        return "3";
    }

    void test(){
        // Accessing instanc method
        System.out.println("s()=\t\t" + s());   // 3
        System.out.println("super.s()=\t" + super.s()); // 2
        System.out.println("((T2)this).s()= " + ((T2)this).s());  // 3; method is resolved at runtime
        System.out.println("((T1)this).s()= " + ((T1)this).s());  // 3; method is resolved at runtime

        //Accessing instance attribute
        System.out.println("\n\nx=\t\t" + x);  // 3
        System.out.println("super.x=\t" + super.x);  // 2
        System.out.println("((T2)this).x=\t" + ((T2)this).x);  // 2; resolved at compile time
        System.out.println("((T1)this).x=\t" + ((T1)this).x);  // 1; resolved at compile time
        System.out.println("((I)this).x=\t" + ((I)this).x);   // 0; resolved at compile time
    }
}

public class SuperAndInstanceMethods {
    public static void main(String[] args) {
        (new T3()).test();
    }
}

其中,

它是 运行 时间 class,在实例方法访问的情况下计算。

它是对象的视图,在字段访问的情况下才算。

转换不会改变对象的 class 类型。我的意思是 ((T1)this) instanceof T3true,如果 this 指向类型 T3 的对象。

那么,字段访问遵循的规则背后的基本原理是什么?实例方法的规则对我来说很有意义。

注意:对我来说,除非有正当理由,否则记住这些规则是一种开销。

只要保持拇指规则为:

  1. 在方法覆盖的情况下,引用变量并不重要,重要的是该变量所引用的实际对象类型。
  2. 在变量 Shadowing/Field 隐藏的情况下,引用变量很重要,而该变量引用的实际对象类型无关紧要。

参考以下示例:

public class Parent {
    String name = "Parent";
    public void printName(){
    System.out.println("Parent Method");
  }
}

public class Child extends Parent {
  String name = "Child";
  public void printName(){
    System.out.println("Child Method");
   }
}

现在将 运行 这个 main() 方法 测试 Class :-

public class Test {

public static void main(String[] args) {
    Parent p = new Parent();
    Child c = new Child();

    System.out.println(p.name); // will print Parent's name
    System.out.println(p.printName());// will call Parent
    System.out.println(c.name); // will print Child's name
    System.out.println(c.printName());// will call Child

    Parent pc = new Child();
    System.out.println(pc.name);// will print Parent's name
    System.out.println(pc.printName());// will call Child
   }
}

这将根据我在上面声明的规则打印以下内容:-

Parent 
Parent Method
Child
Child Method
Parent
Child Method

实例方法通过 V-table 解析,这就是调用运行时类型的方法的方式。没有这样的 table 为字段(或与此相关的静态方法)启用此功能,因此使用编译时类型。

隐藏字段然后做这种事情((T1)this).x非常不寻常,我会避免它,因为我认为它不可读。

并且 java docs 确认:

Within a class, a field that has the same name as a field in the superclass hides the superclass's field, even if their types are different. Within the subclass, the field in the superclass cannot be referenced by its simple name. Instead, the field must be accessed through super, which is covered in the next section. Generally speaking, we don't recommend hiding fields as it makes code difficult to read.

(强调我的)

所以它应该在您要记住的规则列表中排在后面。

您可以为此打开一个 error/warning 一切都很好 IDE,这里是在 IntelliJ 中: