代码之家  ›  专栏  ›  技术社区  ›  Mystical

使用超类vs子类引用子类对象

  •  0
  • Mystical  · 技术社区  · 6 年前

    假设我们有一个 Fruit 班级。

    class Fruit {
        protected String name;
    
        public Fruit(String name) {
            this.name = name;
        }
    
        public void printName() {
            System.out.println(this.name);
        }
    }
    

    和一个 Orange 继承它的类。

    class Orange extends Fruit {
        public Orange() {
            super("orange");
        }
    }
    

    这两者有什么区别:

    Orange o = new Orange();
    o.printName(); // => orange
    

    还有这个:

    Fruit o = new Orange();
    o.printName(); // => orange
    

    它们看起来都有相同的结果,那么有什么区别,什么时候应该使用一个而不是另一个呢?

    2 回复  |  直到 6 年前
        1
  •  2
  •   GhostCat    6 年前

    简单:当您不知道或不关心它是超级类还是子类时,那么您更喜欢超级类变量声明。

    正如您所指出的:由于多态性,方法调用依赖于对象的实际类型,而不是您用来访问它的变量的声明。这是面向对象编程的核心属性之一。

    当您打算调用 只有 存在于子类中。如果没有为父类定义方法,则不能对该方法进行调用!

        2
  •  1
  •   xtratic Rob    6 年前

    这取决于 你想表达什么 . 应该 o 保留任何 Fruit 或者它应该特别持有 Orange ?

    下面是一个类似的模式,它经常出现:

    ArrayList<String> list = new ArrayList<>();
    list .add("element 1");
    // could also use ArrayList specific methods like: list.trimToSize();
    
    // vs
    
    // Even though I am still using an ArrayList
    // I express that I only really care that I have some List
    List<String> list = new ArrayList<>();
    list.add("element 1"); // List interface method
    

    如果我知道我特别需要一个 ArrayList 或者它提供的特性,例如方法 trimToSize() ,我想表达我绝对打算使用 数组列表 . 否则我会使用第二种模式。

    对于第二个模式,您表示您真正需要的只是 List 但您并不真正关心它是什么具体的实现。我认为用这种方式编写代码是一个好主意,通过代码了解和表达您的意图;它还使交换实现变得容易。我可以很容易地把代码改成这个 List<String> list = new LinkedList<>(); 不需要改变任何其他东西。

    另外,对于公开到公共空间的接口方法,您应该使它们尽可能通用(首选父类或接口而不是子类),但是对于实现或内部内容,使用特定的具体类是完全合适的。

    假设你真的想要 橙色 我会用 Orange o = new Orange() 为了表达这一点 o 指的是 橙色 具体地说,而不仅仅是任何 果实 .

        3
  •  0
  •   Praveen E    6 年前

    这在多态性中被称为向上铸造。

    用例:

    假设我有三类水果,橘子和苹果

    class Fruit
    {
        public String getName()
        {
            return "Fruit";
        }
    }
    
    class Orange extends Fruit
    {
        @Override
        public String getName()
        {
            return "Orange";
        }
    }
    
    class Apple extends Fruit
    {
        @Override
        public String getName()
        {
            return "Apple";
        }
    }
    

    现在,如果我想要一个方法,取一个橘子并打印它的名称,我将把它写为

    public static void main(String[] args)
    {
        Orange orange = new Orange();
        printFruitName(orange);
    }
    
    public static void printFruitName(Orange orange)
    {
        System.out.println(orange.getName());
    }
    

    但是,如果我想让这个印刷品印上任何水果的名字,而不仅仅是桔子的名字呢?

    现在我们将改变如下的方法

    public static void main(String[] args)
        {
            Fruit fruit = new Orange();
            printFruitName(fruit);
        }
    
        public static void printFruitName(Fruit fruit)
        {
            System.out.println(fruit.getName());
        }
    

    现在,任何水果的名称都将以printfootname打印。通过说 new Orange(); ,我们说这个物体是橙色的。通过说 Fruit fruit ,我们说holder变量是 Fruit 类型。将来,如果你想改变这个 Orange Apple ,你可以简单地改变 new Orange() new Apple() . 当您不再关心正在创建的基类对象的类型时,就会使用这种多态性。你只需要一个水果。

        4
  •  0
  •   Tejas    6 年前

    以上两个答案回答了你的问题,但我想进一步扩展。

    使用父类变量(fruit o)允许我们在运行时传递子类对象并实现多态性。例如,考虑以下课程

    class ShoppingCart {
       List<Fruit> fruits = new ArrayList<Fruit>();  
       public void addFruits(Fruit f) { 
           this.fruits.add(f);
       }
    }
    

    如果不允许父引用引用子对象,请设想ShoppingCart类的编码。

    class ShoppingCart {
       List<Orange> oranges= new ArrayList<Orange>();    
       public void addOranges(Orange f) { 
           this.oranges.add(f);
       }
       List<Mango> mangoes= new ArrayList<Mango>();  
       public void addMangoes(Mango f) { 
           this.mangoes.add(f);
       }
       .
       .
       .
       .
       .
       .
    }
    

    对于每个水果,我们需要单独的代码来处理。因此,它也提高了代码的可维护性和可读性。 类似地,可以考虑在Fruit和Orange类中添加名为getPrice()的方法。你会意识到它在购物车课程中的重要性。