当我可以通过引用子类访问所有方法时,为什么要引用基类?

我正在学习java概念。 我对javainheritance概念有疑问。 在inheritance中,我们可以将子类实例分配给基类引用,并且我们只能访问基类函数。 我们可以将inheritance层次结构中的任何子类实例分配给基类引用。对于分配给特定基类引用的实例类型,我们只能访问基类函数,但我没有发现任何区别。

任何人都可以给我实际的概念,为什么我们必须将子类实例分配给基类引用? 有什么需要这样做? 相反,我们可以从子类引用只知道访问那些基类函数。

通过考虑层次结构中的特定基类和许多子类来解释。

您可能希望这样做的原因是创建更强大的设计。 以Java中的Collections Framework为例。 你有一个List接口,然后你有两个实现,ArrayList和LinkedList。

您可以编写程序以专门使用LinkedList或ArrayList。 但是,您的程序将依赖于这些特定的实现。

如果您编写的程序依赖于超类型List,那么您的程序可以适用于任何一个List实现。 假设您想要编写一个对List执行某些操作的方法,并且您写了这样的:

public void doSomething(ArrayList a){} 

此方法只能使用ArrayList调用,而不能使用LinkedList调用。 假设你想用LinkedList做同样的事情? 那你复制你的代码了吗? 没有。

  public void doSomething(List l){} 

将能够接受任何类型的List。

这背后的原理是接口的程序而不是实现。 也就是说,List定义了ALL列表的function。

这种用法有很多例子。

inheritance和多态性是面向对象编程的基石,简而言之,它可以用于几个不同的目的:

  • 通过扩展具有特定function的基类来重用代码
  • 通过提供一组抽象function来进行界面设计 ,其中不同的实现针对不同的要求而定制
  • 通过隐藏特定function来封装 ,这在某些上下文中是不需要的

等等。

最后一点还强调,为什么人们可能会使用一组受限制的function,即使在实际实现提供的function不止于此的情况下也是如此。 以Collection接口为例。 通过使用这个接口,我们专注于像isEmptycontainssize这样的方法,而不是实际的实现。

你所描述的是多态性的本质。 这是希腊语中的一个词,意思是“多种forms”。

如果我给你一个像这样的简单层次结构,你可以看到测试代码如何从每个对象中获得不同的计算实现,而不关心它处理的是什么样的Shape:

 public interface Shape { double calculateArea(); } class Circle implements Shape { private double radius; Circle(double r) { this.radius = r; } public double calculateArea() { return Math.PI*radius*radius; } } class Square implements Shape { private double side; Square(double s) { this.side = s; } public double calculateArea() { return side*side; } } // This would be a separate JUnit or TestNG annotated test. public class ShapeTest { @Test public void testCalculateArea() { Map expected = new HashMap() {{ put(new Circle(1.0), Math.PI); put(new Square(1.0), 1.0); }}; for (Shape shape : expected.keySet()) { Assert.assertEquals(expected.get(shape), shape.calculateArea()); } } } 

多态性 。

我是一个给你List 。 关于我实际给你的东西,你需要知道的是它是一个列表,并且具有列表的行为和语义,即你可以把东西放进去,它会维护它们的顺序,你可以迭代它。

你不需要知道的是我如何存储东西,我如何使它们可访问等等。这并不重要。 对于你所关心的一切,它可以是LinkedListArrayList或者全新的东西。 我只想说,我已经选择了一些东西,你可以愉快地使用它。

当你使用inheritance扩展类并添加新行为时,你需要引用子类才能访问它,这是完全正确的。 这两种方法有些互补,但用例不同。

让我们说Vehicle类, CarPlane子类 。 让我们说Vehicle有一个方法move()。

汽车通过上路来超越这个。 飞机通过飞行覆盖了这一点。

为什么move()应该是Vehicle基类的一部分?

因为任何车辆都可以移动()。 但是我们无法在Vehicle中实现move(),因为所有车辆都没有相同的移动方式,即没有共同的行为。 我们仍然希望在基类中使用它,以便我们可以具有多态行为,即我们可以编写如下代码。 正如您所看到的,只有一个名为runVehicle(…)的方法可以在任何Vehicle类上运行。

 void runVehicle(Vehicle v) { v.move(); } Car c=new Car(); runVehicle(c); Plane p=new Plane(); runPlane(p); 

除非API要求,否则没有要这样做。 例如,如果在特定的API或代码库中有一个

 void ReallyUsefulFunction(BaseClass instance) 

您想要使用它,您可以派生一个类BaseClass并在SubClass中实现它的方法。 然后,您现在可以将子类传递给函数。

不过,从技术上讲,你可以实现自己的

 void MyReallyUsefulFunction(MyClass instance) 

它模仿相同的function。 但就像MYYM所解释的那样,代码重用等的好处可能是巨大的,那就是你想要利用多态性的时候。