Why does a superclass reference calling an overridden method appear polymorphic, but not if it takes an overridden member variable?

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

Why does a superclass reference calling an overridden method appear polymorphic, but not if it takes an overridden member variable?

问题

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;
    }
}
英文:
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);
    }
}

The execution result of this class is

男
null

The results are confusing to me. When the superclass calls the overridden method, the results meet my expectations, but when it calls the overridden variable, the results confuse me.so why does a superclass reference calling an overridden method appear polymorphic, but not if it takes an overridden member variable?Here's the entire code.

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;
    }

}

答案1

得分: 2

在子类中,虽然你可以重写一个方法,但无法重写一个字段;实际上,你只是声明了一个具有相同名称的字段。要允许该字段在子类中可见,你可以将其可见性更改为protected或包私有(默认修饰符),如果这两个类在同一个包中的话。演示

public class BClass extends AClass {
 
    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;
    }
 
}
public class AClass {
    protected String name, 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;
    }
 
}
英文:

While you can override a method, you can't override a field in a subclass; you are actually just declaring a field with the same name. To allow the field to also be visible in the child class, you can change its visibility to protected or package private (default modifier), if both classes are in the same package. Demo.

public class BClass extends AClass{
 
    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;
    }
 
}
public class AClass {
    protected String name, 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;
    }
 
}

答案2

得分: 0

根据Java规范,在子类继承时,实例变量不会被子类从超类中覆盖。

英文:

As per the Java specifications, the instance variables are not overridden from a super class by a sub class when it is extended.

答案3

得分: 0

Java不允许你真正覆盖一个字段。

你的BClass实际上有两个名为sex的字段,一个来自AClass,一个来自BClass。而Java的语法并没有真正帮助你找出在像x.sex这样的情况下指的是哪一个字段。就好像你在AClass中定义了一个名为sex_a,在BClass中定义了一个名为sex_b的两个不同字段,只是在这种情况下,对这两个字段的引用都写成了x.sex,没有明确的提示指出是哪一个字段。

在你的情况下:

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

经验丰富的Java开发人员通常会尽力避免这种情况,因为它可能会非常令人困惑。

如果这两个字段在概念上具有相同的含义,就像你在name字段中所做的那样,只在父类中有一个字段,并让子类通过getter和setter访问它(或者通过声明protected可见性来访问字段)。

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

英文:

Java doesn't allow you to really override a field.

Your BClass actually has two fields named sex, one from AClass, and one from BClass. And Java syntax doesn't really help you finding out which one is meant when you write something like x.sex. It's as if you had defined two different fields, sex_a in AClass and sex_b in BClass, only with the complication that references to both are written like x.sex, without a clear hint which of the two is meant here.

In your case:

  • Your BClass instance will have its sex_b initialized, and the sex_a empty (null).
  • aClass.getSex() always calls the most specific method, based on the instance's runtime class, being BClass. So it chooses the method from BClass, returning sex_b, and thus prints the sex.
  • aClass.sex accesses one of the two sex fields, depending on the variable's compile-time-deducible type, in your case being AClass. So it prints the sex_a value, being null.

Seasoned Java developers typically do their best to avoid this situation, as it can be very confusing.

If the two fields conceptually have the same meaning, do it as you did with the name field, having only one field in the parent class, and have the subclass access it via getters and setters (or by declaring protected visibility for the field).

If the two fields have conceptually different meanings (can an object have two different sexes?), use different names.

huangapple
  • 本文由 发表于 2020年8月12日 22:41:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63378926.html
匿名

发表评论

匿名网友

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

确定