使用子类的类型或其超类的类型创建子类对象

Creating a subclass object with the subclass's type or its superclass's type

假设我有一个名为 Person 的超类,它具有私有实例变量名称和年龄,还有一个名为 Student 的子类,它添加了一个名为 school 的实例变量。 我的父构造函数是:

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

我的子构造函数是:

public Student(String name, int age, String school) {
        super(name, age);
        this.school = school;
    }

我可以通过以下方式使用 Student 构造函数创建对象:

Student test = new Student("Joe", 20, "Whosebug");
Person test = new Student("Joe", 20, "Whosebug");

我不确定在这种情况下有什么区别,或者在一般情况下有什么区别,或者两者是否具有某些优势。

在这样的小程序中,很难看到继承的好处,特别是以相同方式创建学生和人员的能力。

但是,想一想您有一个排序算法,可以对类型 'Person' 进行排序,并且只对那个类型进行排序。

Person p = new Person(name, age);
ArrayList<Person> list = new ArrayList<Person>();

Sort(list);

排序方法只接受类型为 Person 的列表。

后来您意识到能够对学生进行排序会很好,但是您不想重写排序算法。由于您的排序算法仅针对 'Person' 类型进行编码,因此您现在可以传递一个学生但包含在一个人中。您还可以重写比较方法来对学生进行不同于人的排序。这是一个简单的例子,还有很多。

Person s = new Student(name, age, school);
list.add(s);
Sort(list);

以上均有效,无需重写代码。 (假设姓名、年龄和学校是正确的类型等。)。

您可能希望将引用类型设置为 Person 有几个原因。一般来说,它更抽象,在编码和阅读代码时需要更少的思考。

  • 您的代码的未来读者不必想知道为什么类型需要 Student,他们可能会思考并花费太多时间来弄清楚是否使用了 Student 特定操作或者为什么没有。
  • 它限制了 operations/fields 作为您的编码的数量,因此可供查看和调用的内容的复杂性较低。因此,调用错误方法导致错误的可能性较小,您需要的只是可用的。
  • 您可能需要稍后更改您的 Person 实例的子类(就像您引入 Teacher extends Person 一样),如果您选择将变量声明为 Person,则你肯定知道除了实例化之外你不需要更改任何其他代码行,因为你没有调用 Student 中 redefined/rewritten/wrapped 的任何等效方法(我想这通常很糟糕如果存在此类方法,则进行设计,但可能)。