为什么调用重写方法的超类引用看起来是多态的,但是如果它需要重写成员变量,为什么不是呢?

问题描述

package main.java;

public class Demo {
        public static void main(String[] args) {
        BClass bClass=new BClass("han","男");
        AClass aClass=bClass;
        System.out.println(aClass.getSex());
        System.out.println(aClass.sex);
    }
}

此类的执行结果为

男
null

结果令我感到困惑。当超类调用重写方法时,结果符合我的期望,但是当它调用覆盖变量时,结果使我感到困惑。那么为什么调用重写方法的超类引用看起来是多态的,但是如果它采用了重写成员变量,为什么不是呢?这是完整的代码

package main.java;

public class Demo {
        public static void main(String[] args) {
        BClass bClass=new BClass("han","男");
        AClass aClass=bClass;
        System.out.println(aClass.getSex());
        System.out.println(aClass.sex);
    }
}
package main.java;

public class AClass {
    private String name;

    public String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

}

package main.java;

public class BClass extends AClass{

    private String sex;

    public BClass(String name,String sex) {
        this.sex = sex;
        super.setName(name);
    }

    @Override
    public String getSex() {
        return sex;
    }

    @Override
    public void setSex(String sex) {
        this.sex = sex;
    }

}

解决方法

虽然您可以覆盖方法,但不能覆盖子类中的字段;您实际上只是在声明一个具有相同名称的字段。为了使该字段在子类中也可见,如果两个类都在同一包中,则可以将其可见性更改为class MainActivity : AppCompatActivity(),View.OnClickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) num1.setOnClickListener(this) num2.setOnClickListener(this) num3.setOnClickListener(this) num4.setOnClickListener(this) num5.setOnClickListener(this) num6.setOnClickListener(this) num7.setOnClickListener(this) num8.setOnClickListener(this) num9.setOnClickListener(this) } override fun onClick(view: View?) { val currentValue = mainText.text val numberPressed = (view as Button).text mainText.setText("${currentValue}${numberPressed}") } } 或包私有(默认修饰符)。 Demo

protected
,

按照Java规范,扩展实例类时,实例变量不会从超类中被子类覆盖。

,

Java不允许您真正覆盖字段。

您的BClass实际上有两个名为sex的字段,一个来自AClass,一个来自BClass。 Java语法并不能真正帮助您在编写x.sex之类的代码时找出含义是什么。就像您已经定义了两个不同的字段一样,sex_a中的AClasssex_b中的BClass,只是复杂的是对这两个字段的引用都像x.sex ,但没有明确提示这两个是这里的意思。

在您的情况下:

  • 您的BClass实例将初始化其sex_b,并且sex_a为空(空)。
  • aClass.getSex()始终根据实例的运行时类调用最特定的方法BClass。因此,它从BClass中选择方法,返回sex_b,从而打印出性别。
  • aClass.sex访问两个sex字段之一,具体取决于变量的编译时可推导类型,在您的情况下为AClass。因此它将打印sex_a值,为null。

经验丰富的Java开发人员通常会尽力避免这种情况,因为这可能会造成混乱。

如果两个字段在概念上具有相同的含义,请像对待name字段那样进行操作,在父类中只有一个字段,并让子类通过getter和setter(或通过声明protected字段的可见性。

如果这两个字段在概念上具有不同的含义(一个对象可以具有两种不同的性别吗?),请使用不同的名称。