Java虚拟方法调用

4

假设我有以下代码:

public class Employee
{
    public int salary = 2000;
    public void getDetails() {...}
}

public class Manager extends Employee
{
    public int salary = 5000;
    public int allowance = 8000;
    public void getDetails() {...}
}

还需要一个 main() 函数,它执行以下操作:

Employee emp = new Employee();
Manager man = new Manager();
emp.getDetails(); // method of Employee called, output ok.
man.getDetails(); // method of Manager called, output ok.

Employee emp_new = new Manager();
emp_new.getDetails(); // method of Manager called, ok.
System.out.println(emp_new.allowance); // problem, as Employee doesn't know about allowance. Ok

// the problem
System.out.println(emp_new.salary); // why 2000 and not 5000?

这本书中提到:“在运行时,您将获得与变量引用的对象相关联的行为”。当我调用方法getDetails时,我了解了Manager类的行为,但是当我访问属性salary时,我获得的是变量而不是对象的行为。为什么会这样呢?

4个回答

6

字段没有多态性。子类中的字段会隐藏超类中同名的字段,但如果您使用超类类型的变量Employee来访问该字段,则会得到超类的字段。

我不认为在超类和子类中都声明具有相同名称的字段有意义。如果子类可以访问超类的字段,它就不应该声明一个同名的字段。

如果必须在超类和子类中声明同名字段,可以通过getter和setter方法访问该字段来实现多态性。您可以在子类中重写getter和setter方法,以便它们访问子类的字段而不是超类的字段。

public class Employee
{
    private int salary = 2000;
    public void getSalary() {
        return salary;
    }
}

public class Manager extends Employee
{
    private int salary = 5000;
    @Override
    public void getSalary () {
        return salary;
    }
}

...

Employee emp_new = new Manager();
System.out.println(emp_new.getSalary()); // will print 5000

2
这种行为是由于“隐藏字段”造成的。
来自JLS 8.3. Field Declarations 如果类声明了一个具有特定名称的字段,则该字段的声明被认为隐藏了类的超类和超接口中具有相同名称的任何可访问字段的声明。
在这方面,字段的隐藏与方法的隐藏(§8.4.8.3)不同,因为字段隐藏中没有区分静态字段和非静态字段,而在方法隐藏中区分静态方法和非静态方法。
如果一个字段被隐藏了,可以使用限定名(§6.5.6.2)(如果它是静态的),或者通过包含关键字super(§15.11.2)或向上转型到父类类型的字段访问表达式来访问该字段。
所以在你的情况下,你正在直接访问通过Employee类的引用变量存储Manager对象(向上转型到父类类型)的隐藏变量salary
所以它将打印出“Employee”类的隐藏“salary”,而不是“Employee”类的“salary”。

0

Employee类是父类,调用的属性将返回父类属性的值(因为它们具有相同的名称和类型)。


0

因为这不是虚方法调用

引用如下:

在运行时,您会获得与变量所引用的对象相关联的行为

您忽略了变量salary不是行为的一部分 - 它是对象的状态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接