2010-12-10 38 views
2

好吧,我已经通过googleing查找了文档,但是我还没有找到任何真正描述了我想要回答的内容,所以在这里我问你们。了解Java中的继承和抽象类

所以我得到继承,以及它是如何工作的。我遇到问题时有时会看到最初定义为一种类型的对象,并设置为不同类型,并且我不明白到底发生了什么。这里有一个例子:

说我有一个类的动物,并扩展动物类猫和狗。猫,动物和狗都有一种方法说话()为猫打印“喵”和狗打印“woof”和为动物“不会说话”。

好吧,最后这里是我的问题。如果一个猫(c)然后运行Animal a = c ;?如果我运行a.speak();会发生什么情况;?哪种说法叫做?当我改变那种类型时究竟发生了什么?我会有任何真正的理由来使用它吗?

就抽象方法而言,我的问题是具有它们的意义究竟是什么?在我看到的例子中,他们已经被放入了超类,而它们下面的类定义了确切的行为。把一个抽象方法放在一个超类中是需要它下面的所有类来实现它的吗?

感谢您的帮助!

回答

3

究竟发生如果一只猫 (c),然后运行动物a = c;?如果我运行a.speak(),会发生什么 ?哪个 说法叫做? 究竟发生了什么,当我改变 这样的类型时呢?我会有任何真正的原因 使用这个?

永远是真实的类的方法,例如在这种情况下,猫的speak()方法。

至于抽象方法去,我 问题是,究竟是什么让他们的点 ?

他们确保,例如,每个动物都有一个方法walk(),你可以在每个动物身上呼叫。这是一个保证,说“每个Animal对象有这种方法,你不必关心它”。

在我 看到他们已经投入超类 和其下的类定义的 具体行为的例子。通过把一个抽象的 方法放在一个超级类中是一个 需要它下面的所有类去实现它的 吗?

要实现它或抽象,也是的。

2

Cat c = new Cat(); 动物a = c; a.speak()将打印喵。

请检查java polymorphism了。

约抽象类:

当一个抽象类的子类是, 子类通常提供 实现对所有的在它的父类的 抽象方法。 但是,如果不是,则子类 也必须声明为抽象。

JLS的第5.2节解释了为什么Cat可分配给Animal。 (请注意,Animal不是隐含地分配给Cat因为Cat是一个“更具体的”种类; CatAnimalAnimal亚型是Cat超类型)编译时引用类型的值的

分配S(源)到编译时引用类型T(目标)的一个变量,检查如下:

  • 如果S是一个类类型:
    • 如果T是一个类类型,则S必须与T相同,或S必须是T的子类,否则会发生编译时错误。
    • 如果T是一个接口类型,那么S必须实现接口T,否则会发生编译时错误。
    • 如果T是一个数组类型,则会发生编译时错误。
  • 如果S是一个接口类型:
    • 如果T是一个类类型,则T必须是Object,或编译时会出现误差。
    • 如果T是一个接口类型,那么T必须是S的接口或S的超接口,否则会发生编译时错误。
    • 如果T是一个数组类型,则会发生编译时错误。
  • 如果S是一个数组类型SC [],也就是说,类型SC的分量的数组:

[不再赘述]

+0

子类型多态性解释'a.speak()',但它不能解释为什么Cat可以隐式地赋值给Animal。一些更多的细节(JLS链接会很好),这将是一个很好的答案。 – 2010-12-10 23:50:07

+0

第5.2节包含*可分配*要求。 http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#184206。如下检查“编译时参考类型S(源)的值到编译时参考类型T(目标)的变量的赋值如下”。 – 2010-12-10 23:57:03

+2

由于Cat延伸Animal,因此'Cat'可以隐式分配给'Animal'。一般来说,如果'A'扩展了'B',那么'A'可以被赋予'B'。如果'A'实现了一个接口'C',那么它可以被分配给'C'。这些关系是可传递的,所以如果'X'扩展'Y'继而扩展'Z',那么'X'的一个实例可以被分配给一个被声明为'X','Y'或'Z'中任一个的变量。 。 – 2010-12-11 00:03:37

0

根据你的背景,C++或Java的东西可能会变得非常混乱。

在C++中,虚拟函数的概念在运行时被查找来决定函数属于哪个实际类。还有非虚函数将根据变量类型调用。

在Java中,虽然所有的方法本质上是虚拟的,这意味着Java方法在运行时总是抬头一看,这个过程被称为运行时多态性

这样做的好处是这样

class Animal{ 
    public void type(){ 
     System.out.println("animal"); 

    } 
} 

class Dog extends Animal{ 
    public void type(){ 
     System.out.println("dog"); 
    } 
} 

class Cat extends Animal{ 
    public void type(){ 
     System.out.println("cat"); 
    } 
} 

public class Driver{ 
    public static void main(String[] args){ 
     Animal[] animals = new Animal[3]; 
     animals[0] = new Animal(); 
     animals[1] = new Dog(); 
     animals[2] = new Cat(); 

     for(Animal animal: animals){ 
      animal.type(); 
     } 
    } 
} 

这将输出

animal 
dog 
cat 
0

据我了解,您使用接口来解耦代码。您想要针对接口进行编程,而不是实现:What does it mean to "program to an interface"?。你将使用一个抽象类来实现对所有实现类来说都是微不足道的函数,所以你不需要把它写在所有的实现类中。

1

说我有一个类动物,并且教授扩展动物的类猫和狗。猫,动物和狗都有一种方法说话()为猫打印“喵”和狗打印“woof”和为动物“不会说话”。

好吧,最后这里是我的问题。如果制作一只猫(c)然后运行Animal a = c;究竟发生了什么?如果我运行a.speak();会发生什么?哪个speak()方法被调用?当我改变那种类型时究竟发生了什么?我会有任何真正的理由来使用它吗?

Java中的对象完全知道它们被创建为何种类型;它实际上是一个隐藏字段(可以使用Object.getClass()方法检索)。而且,所有非静态方法的解析都是从最具体的类的方法定义开始的,然后进入最通用的类​​(Object);因为在Java中只有一次实现的继承,所以这是一个简单的搜索。 Cat知道它是Animal的一个子类型,它是Object的子类型,并且c知道它是一个Cat,与变量的类型无关。

当你做任务,如果该值的已知类型被分配编译检查是被分配到或其子的一个类型。如果是,分配工作。如果不是,你需要一个明确的转换(在运行时放入适当的类型检查;转换不能破坏Java的类型系统,它们可能会使其变得更难看)。它并没有改变方法查找仍然动态完成的事实,并且对象仍然知道它确实是什么类型;所有的程序正在做的是忽略一些信息。 如果您了解C++,那么请将Java视为仅具有虚拟方法(以及静态方法),并且将查找处理到vtable中非常简单,因为继承钻石或其他恶意案例没有问题。

当处理接口的实现时,除了执行更复杂的查找(即先前查找vtable中的索引,然后再继续处理之前),它几乎是相同的。然而,要有一个实现接口的对象意味着必须有一个完全实现接口的类,那么这个接口再次相对简单。请记住,所有非常复杂的东西都是在编译时完成的;在运行时,事情在所有情况下都是相对直接的。

那么,你会利用这一切吗?那么,你应该应该(你真的很难避免真正的代码)。从界面或超类定义合同的角度来看,这是一种很好的风格,在这种情况下,子类遵守合同,而不需要调用者知道细节。 Java库非常重要,特别是因为合同类型细节的可见性及其履行情况可能不同。所有的客户端代码都知道对象服从合同,它是给定的类型。

+0

你提出的其他问题几乎被其他人回答,并且在许多其他面向对象的语言中有几乎相同的答案。 – 2010-12-11 01:22:15