【问题标题】:Why should I reference the base class when I can access all the methods just as well by referencing the subclass?当我可以通过引用子类来访问所有方法时,为什么还要引用基类?
【发布时间】:2011-01-08 15:08:43
【问题描述】:
我正在学习 Java 概念。
我对java继承概念有疑问。
在继承中,我们可以将子类实例分配给基类引用
并且我们只能访问基类函数。
我们可以将继承层次结构中的任何子类实例分配给基类引用。对于分配给特定基类引用的实例类型,我们只能访问基类函数,我没有发现任何区别。
谁能给我一个实际的概念
为什么我们必须将子类实例分配给基类引用?
有什么必要这样做?
相反,我们可以从仅知道子类引用的子类引用中访问那些基类函数。
通过考虑特定的基类和层次结构中的许多子类来解释。
【问题讨论】:
标签:
java
oop
inheritance
polymorphism
【解决方案1】:
您可能想要这样做的原因是为了创建更强大的设计。以 Java 中的集合框架为例。你有一个 List 接口,然后你有两个实现,ArrayList 和 LinkedList。
您可以编写程序以专门使用 LinkedList 或专门使用 ArrayList。但是,您的程序随后取决于这些特定的实现。
如果您将程序编写为依赖于超类型 List,那么您的程序可以适用于任一 List 实现。假设您想编写一个对 List 执行某些操作的方法,并且您编写了以下代码:
public void doSomething(ArrayList a){}
这个方法只能用 ArrayList 调用,不能用 LinkedList 调用。假设您想对 LinkedList 做同样的事情?然后你复制你的代码吗?没有。
public void doSomething(List l){}
将能够接受任一类型的列表。
这背后的原理是程序到接口而不是实现。即List定义了所有列表的功能。
有很多这种用法的例子。
【解决方案2】:
继承和多态是面向对象编程的基石,它们有几个不同的用途,简而言之:
-
代码重用通过扩展具有特定功能的基类,
-
界面设计通过提供一组抽象的功能,其中不同的实现是针对不同的需求量身定制的,并且
-
封装通过隐藏在某些情况下不需要的特定功能
等等。
最后一点还强调了为什么人们可能会使用一组受限的功能,即使在实际实现提供的功能不止这些的情况下也是如此。以Collection 接口为例。通过使用这个接口,我们专注于isEmpty、contains 或size 等方法,而不是实际的实现。
【解决方案3】:
您所描述的是多态性的本质。这是一个来自希腊语的词,意思是“多种形式”。
如果我给你一个像这样的简单层次结构,你可以看到测试代码如何从每个对象中获得不同的计算实现,而不用关心它正在处理什么样的 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<Shape, Double> expected = new HashMap<Shape, Double>()
{{
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());
}
}
}
【解决方案4】:
Polymorphism.
我是一个给你List<String>的方法。关于我实际上给你的东西,你只需要知道它是一个列表,并且具有列表的行为和语义,即你可以把东西放进去,它会保持它们的顺序,你可以迭代它.
你不需要知道我是如何存储东西的,我是如何让它们被访问的,等等。这并不重要。对于您所关心的,它可能是LinkedList<String>、ArrayList<String> 或全新的东西。可以这么说,我已经挑选了一些东西,你可以愉快地使用它。
您是绝对正确的,当您使用继承来扩展类并添加新行为时,您需要引用子类才能访问它。这两种方法有些互补,但用例不同。
【解决方案5】:
假设 Vehicle 是 base 类,而 Car 和 Plane 是 子类时间>。假设 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);
【解决方案6】:
没有真正的需要这样做,除非 API 需要它。例如,如果在特定的 API 或代码库中有一个
void ReallyUsefulFunction(BaseClass instance)
如果您想使用,您可以从 BaseClass 派生一个类并在 SubClass 中实现其方法。然后您现在可以将子类传递给函数。
不过,从技术上讲,您可以实现自己的
void MyReallyUsefulFunction(MyClass instance)
模仿相同的功能。但就像 MYYM 所解释的那样,代码重用等的好处可能是巨大的,这就是你想要利用多态性的时候。