【问题标题】:Inheritance super variables继承超变量
【发布时间】:2014-10-06 12:00:09
【问题描述】:

我正在编写一个简单的程序,其中我有一个由子类CustomerEmployee 继承的超类Person(它们继承了变量IDnamesurname) .

public class Person {

    int id;
    String name;
    String surname;

    public Person() {}

    public Person(int i, String n, String s) {
        id = i;
        name = n;
        surname = s;
    }
}
public class Employee extends Person implements Serializable {

    String username;
    String password;
    String date;
    int hpw;
    int recordSold;
    float hourPay;

    public Employee() {}

    public Employee(String u, String n, String s, String p, int i, int h, String d, int rSold, float hPay) {
        username = u;
        super.name = n;
        super.surname = s;
        password = p;
        super.id = i;
        hpw = h;
        date = d;
        recordSold = rSold;
        hourPay = hPay;
    }
}

但是问题就在这里:当我尝试通过我的主类获取变量 IDnamesurname 时,它们无法返回 (0,null,null)。为什么是这样?我的子类中有 get-Methods 应该返回超级变量,但它们不是。感谢您的时间和耐心。

public String getName() {
    return super.name;
}

更新: 好的,所以我整理了 Employee 类构造函数中的 super(id,name,surname) 。我还删除了员工类中的所有 getter 和 setter,因为它们是从 Person 超类继承的(如果我错了,请纠正我?..)

人物超类:

public class Person {
private int id;
private String name;
private String surname;

public Person () {
}

public Person(int i, String n, String s) {
    this.id = i;
    this.name = n;
    this.surname = s;
}

public void setID(int i) {
    this.id = i;
}
public void setName(String n) {
    this.name = n;
}
public void setSurname(String s) {
    this.surname = s;
}

public int getID() {
    return id;
}
public String getName() {
    return name;
}
public String getSurname() {
    return surname;
}

}

员工子类:

import java.io.*;
public class Employee extends Person implements Serializable {

protected String username;
protected String password;
protected String date;  
protected int hpw;
protected int recordSold;
protected float hourPay;

public Employee() {
    super();
}

public Employee(int i, String u, String n, String s, String p, int h, String d, int r, float hP) {
    super(i,n,s);
    username = u;
    password = p;
    date = d;
    hpw = h;
    recordSold = r;
    hourPay = hP;
}

public void setUser(String u) {
    username = u;
}

public void setPassword(String p) {
    password = p;
}

public void setHWeek (int h) {
    hpw = h;
}

public void setDate (String d) {
    date = d;
}

public void setRSold (int r) {
    recordSold = r;
}

public void setHPay (float p) {
    hourPay = p;
}

public String getUser() {
    return username;
}

public String getPassword() {
    return password;
}

public int getHWeek() {
    return hpw;
}

public String getDate() {
    return date;
}

public int getRSold() {
    return recordSold;
}

public float getHPay() {
    return hourPay;
}

但是,当我运行主程序时,ID、name 和 surname 变量仍然为空,它们没有被超类返回。请问我错过了什么吗?谢谢

【问题讨论】:

  • 请说明您是如何实例化Employee 对象的
  • 您能否提供一个显示问题的可运行程序?
  • @Jens 说的,问题不在这段代码
  • @TheLostMind 他明确在Employee() 中,Java 为他调用了默认的超级构造函数。但是它仍然可以工作
  • @LionC - 我们将知道他是否向我们展示了他的实例化代码。

标签: java variables inheritance super


【解决方案1】:

继承仅适用于方法NOT变量。在直接访问超类变量的子类中实现方法也是不好的做法。你最好在你的超类中实现访问方法。由于继承,这些方法也将在子类中可用。

另一件事是实例变量的可见性。您正在使用“包范围”的默认可见性。因此,如果您的子类不在同一个包中,它们就无法访问这些变量。如果您使用“私有”或“受保护”可见性,访问变量会更安全。

另一点是您未正确初始化对象。调用子类构造函数也必须调用超类构造函数,因为您的 Employee 对象依赖于您的 Person 对象提供的功能。对这个原理有更科学的描述:

Barbara Liskov - Liskov 替换原理

public class Person {

    private int id;
    private String name;
    private String surname;

    public Person() {}

    public Person(int i, String n, String s) {
        id = i;
        name = n;
        surname = s;
    }

    public int getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public int getSurname() {
        return this.surname;
    }
}

为超类实例变量添加访问方法并将可见性设置为私有。

public class Employee extends Person implements Serializable {

    private String username;
    private String password;
    private String date;
    private int hpw;
    private int recordSold;
    private float hourPay;

    public Employee() {}

    public Employee(String u, String n, String s, String p, int i, int h, String d, int rSold, float hPay) {
        super(id, name, surname);
        this.username = u;
        this.password = p;
        this.hpw = h;
        this.date = d;
        this.recordSold = rSold;
        this.hourPay = hPay;
    }
}

调用超类的构造函数进行超类的初始化。

【讨论】:

  • 是的,使用默认的超级构造函数不是很好的风格。是的 OP 应该改变这一点。不,这不是问题的原因,OP 的 Employee(String, String...)-constructor 以他希望的方式工作。
  • “继承仅适用于方法而不适用于变量”是什么意思?您显然可以在子类中使用来自超类的变量
【解决方案2】:

您的代码应如下所示:

public class Person {

  private int id;
  private String name;
  private String surname;

  public Person (int id, String name, String surname) {
    this.id = id;
    this.name = name;
    this.surname = surname;
  }

  public int getId() {
    return id;
  }

  ... //similarly for getName() and getSurname()
}


public class Employee extends Person {

  private String username;
  private String password;
  private String date; 
  private int hpw;
  private int recordSold;
  private float hourPay;

  public Employee (int id, String name, String surname, String username, String password, String date, int hpw, int recordSold, float hourPay) {
    super(id, name, surname);

    this.username = username;
    ... //similarly for other parameters.
  }
 }

重要的是super(id, name, surname)

编辑

lionc 声称我没有回答这个问题,这是真的。我这样做是因为the original poster 似乎是 Java 的新手,因此可能会问“错误”的问题。我应该在我最初的回复中强调这一点。鉴于我的答案目前被标记为最佳,我相信我做出了正确的决定。

【讨论】:

  • 虽然你说这会是更好的风格是对的,但 OP 的做法在技术上是正确的
  • @LionC 在您的电气装置中使用蓝色电线作为带电导体在技术上是正确的 - 电线的颜色不会改变电线的特性,但任何人都会必须使用/维护您的安装将不会在他们发现它时表示感谢,并且 10 年后您也将不会对此感到高兴。
  • @GermannArlington 这是 100% 正确的,但这不是问题的重点,因此不应该是答案的唯一内容,因为 OP 询问的问题在于实例化的代码对象而不是他发布的代码。但是人们回答他,说他发布的代码是错误的,他应该做不同的事情,这样可以解决问题,而事实并非如此。
  • 抱歉,如果我没有提出正确的问题和/或以正确的方式提出。实际上,我对这些论坛非常陌生,并尽我所能直截了当并清楚地表明了这一点。感谢您的所有时间和耐心,谢谢。
  • @Logan,没问题!提出“正确”的问题非常困难。尤其是当有潜在的问题阻止你进入问题的核心时。我没有解释this 的用法,你的用法很糟糕。 this 运算符使您能够区分作为函数输入提供的变量和类中的变量。因此,我可以写成f(int i) { this.i = i; },而不是f(int ii) { i = ii; }(即类变量和函数变量可以同名)。写f(int i) { this.j = i; }(就像你所做的那样)是不好的风格。
【解决方案3】:

您尚未初始化这些变量,这就是它返回这些变量的默认值的原因。在 java 中,以下是变量的默认值。

int -> 0
String -> null (because  String is Object in Java)

【讨论】:

    【解决方案4】:

    您在两个类中都定义了这些属性,以便在子类中覆盖它们。此外,您的 Employee 构造函数不是它应该的方式。您应该将改编后的超级构造函数称为您的第一个语句。

    public class Person {
    
        protected int       id;
        protected String    name;
        protected String    surname;
    
        public Person(int id, String name, String surname) {
            this.id      = id;
            this.name    = name;
            this.surname = surname;
        }
    }
    
    public class Employee extends Person implements Serializable {
        private String  username;
        private String  password;
        private String  date;
        private int     hpw;
        private int     recordSold;
        private float   hourPay;
    
        public Employee(String username, String name, String surname, String pswd, int id,
                int hpw, String date, int rSold, float hPay) {
            super(id,name,surname);
            this.username   = username;
            this.password   = pswd;
            this.hpw        = hpw;
            this.date       = date;
            this.recordSold = rSold;
            this.hourPay    = hPay;
        }
    }
    

    在您的构造函数中,我认为最好的做法是为您的参数提供与属性名称相同的名称,以初始化和区分它们,这要感谢this。有些人也使用相同的名称,只是他们在类的所有成员的开头添加了_。在任何情况下,当它们所代表的变量具有特殊含义(姓氏、姓名)时,不要使用“s”、“n”等无意义的名称。例如,为没有任何特定语义的局部变量保留这些名称(n 将是一个整数,s 将是一个字符串......)。

    【讨论】:

    • 首先,请写出完整的答案,不要发布甚至包含不完整且您将尽快完成的声明的答案。其次,正如其他答案中已经提到的那样,是的,这会是更好的风格,但这不是技术问题,因为隐式调用默认构造函数并设置 super.fields 有效
    • 我还删除了在大多数情况下不推荐的默认构造函数(即使有时需要它们,例如 JPA)。他这样做的方式使用默认构造函数实际上是他问题的最可能原因。这不仅仅是一个风格的答案。
    • 但这不是问题。 OP 发布的代码有效。
    • 他的属性没有初始化...意思是什么?他调用了默认构造函数。
    • 他明确地调用了 Person 的默认构造函数(好吧,Java 为他做了)。但是他自己在 Employee-constructor 中设置字段,这应该可以工作。所以使用 Person() 默认构造函数不是问题。只需将 OP 的代码 c&p 到 IDE 中并自己测试,它就可以工作
    【解决方案5】:

    在您的示例中,您不需要使用super 来访问超类中定义的属性,因为您正在使用它们的包可见性(并且两者似乎都在同一个包中)。

    但是,这不是编写 Java 代码的正确方法。

    您应该定义属性的可见性。在大多数情况下,建议使用 private 可见性并定义 getter 和 setter 方法来访问它们:

    public class Person {
        private int id;
        private String name;
        private String surname;
    
        public Person() {}
    
        public Person(int id, String name, String surname) {
            this.id = id;
            this.name = name;
            this.surname = surname;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        // And so on...
    }
    

    在子类中,您只需调用 getId()setId(...) 即可访问 Id 属性。无需致电super.getId()。由于Employee 扩展了Person,它可以访问其所有公共的、受保护的(如果它们在同一个包中,还有包)属性和方法。

    这意味着在您当前的代码中,您可以简单地编写 name = n 而不是 super.name = n

    public class Employee extends Person implements Serializable {
        private String username;
        private String password;
        private String date;
        private int hpw;
        private int recordSold;
        private float hourPay;
    
        public Employee() {}
    
        public Employee(String username, String name, String surname, String password, int id, int hpw, String date, int rSold, float hPay) {
            super(id, name, surname);
            this.username = username;
            this.password = password;
            this.hpw = hpw;
            this.date = date;
            this.recordSold = rSold;
            this.hourPay = hPay;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        // And so on...
    }
    

    现在要使用这些类,您可以编写如下代码:

    Employee e = new Employee("user3149152", "Ulrich", "Ser", "passwd", 1234, 0, "2014/08/13", 0, 0);
    System.out.println("Employee " + e.getName() + ' ' + e.getSurname() + " has for id " + e.getId() + '.'); 
    

    作为参考,此代码甚至适用于您当前的代码。 它打印:

    Employee Ulrich Ser has for id 1234.
    

    【讨论】:

    • 这会是更好的风格,但 OP 的做法(隐式调用默认的超级构造和设置 super.fields)在技术上是有效的,所以这并不是问题的真正答案
    • 我认为编写适当的代码是开始学习一门语言的最佳方式。
    • 在这个问题上没有人会反对你,但这不是问题的重点
    • 不确定他是否在寻找技术答案。
    猜你喜欢
    • 2011-03-25
    • 2023-01-19
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    相关资源
    最近更新 更多