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

时间:2011-01-08 15:08:44

标签: java oop inheritance polymorphism

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

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

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

6 个答案:

答案 0 :(得分:5)

您可能希望这样做的原因是为了创建更强大的设计。以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列表的功能。

这种用法有很多例子。

答案 1 :(得分:3)

继承和多态是面向对象编程的基石,简而言之,它有几个不同的用途:

    通过扩展具有特定功能的基类 代码重用
  • 接口设计,提供一组抽象功能,根据不同的需求定制不同的实现,
  • 封装隐藏特定功能,在某些情况下不需要

等。

最后一点还强调,为什么人们可能会使用一组受限制的功能,即使实际实现提供的功能不止于此。以Collection接口为例。通过使用此界面,我们专注于isEmptycontainssize等方法,但不关注实际实现。

答案 2 :(得分:2)

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

如果我给你一个像这样的简单层次结构,你可以看到测试代码如何从每个对象中获得不同的计算实现,而不关心它处理的是什么样的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());
        }       
    }
}

答案 3 :(得分:1)

Polymorphism

我是一种为您提供List<String>的方法。关于我实际给你的东西,你需要知道的是它是一个列表,并且具有列表的行为和语义,即你可以把东西放进去,它会保持它们的顺序,你可以迭代它

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

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

答案 4 :(得分:1)

我们说广告基础类,而 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);

答案 5 :(得分:0)

没有真正的需要来做到这一点,除非API要求它。例如,如果在特定的API或代码库中存在

void ReallyUsefulFunction(BaseClass instance)

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

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

void MyReallyUsefulFunction(MyClass instance)

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