繁体   English   中英

了解Java中的继承和抽象类

[英]Understanding inheritance and abstract classes in Java

好的,所以我一直在通过Google搜索来寻找文档,但是我还没有找到任何能真正描述我要回答的内容,所以在这里我问你们。

因此,我得到了继承及其运作方式。 我遇到的问题是有时我看到一个对象最初定义为一种类型,并设置为另一种类型,但我不完全了解发生了什么。 这是一个例子:

假设我有动物课,而猫和狗课又有动物课。 猫,动物和狗都有方法speak(),用于猫打印“喵”,用于狗打印“ woof”和用于动物“不会说话”。

好了,所以这终于是我的问题了。 如果让一只猫(c)然后运行Animal a = c;会发生什么? 如果我运行a.speak();会发生什么? 叫哪种说话方法? 当我改变这样的类型时到底发生了什么? 我会有任何真正的理由使用它吗?

就抽象方法而言,我的问题是拥有它们的真正意义是什么? 在示例中,我已经看到它们已放入超类中,并且其下的类定义了确切的行为。 通过将一个抽象方法放在一个超类中,是不是要求其下的所有类都可以实现它呢?

感谢你的帮助!

如果让一只猫(c)然后运行Animal a = c;会发生什么? 如果我运行a.speak();会发生什么? 叫哪种说话方法? 当我改变这样的类型时到底发生了什么? 我会有任何真正的理由使用它吗?

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

就抽象方法而言,我的问题是拥有它们的真正意义是什么?

他们确保例如每个动物都有一个可以调用每个动物的方法walk() 保证书上写着“每个Animal物体都有这种方法,您不必在意”。

在示例中,我已经看到它们已放入超类中,并且其下的类定义了确切的行为。 通过将一个抽象方法放在一个超类中,是不是要求其下的所有类都可以实现它呢?

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

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

请检查出Java多态性

关于抽象类:

当抽象类被子类化时,该子类通常为其父类中的所有抽象方法提供实现。 但是,如果没有,则子类也必须声明为抽象。

JLS的5.2节解释了为什么Cat可分配给Animal (请注意,由于Cat是“更特定的”种类,所以不能将Animal隐式分配给CatCatAnimal的子类型,而AnimalCat的超类型)

如下检查将编译时参考类型S(源)的值分配给编译时参考类型T(目标)的变量:

  • 如果S是类类型:
    • 如果T为类类型,则S必须与T属于同一类,或者S必须为T的子类,否则会发生编译时错误。
    • 如果T是接口类型,则S必须实现接口T,否则会发生编译时错误。
    • 如果T是数组类型,则发生编译时错误。
  • 如果S是接口类型:
    • 如果T是类类型,则T必须是Object ,否则会发生编译时错误。
    • 如果T是接口类型,则T必须是与S相同的接口或S的超接口,否则会发生编译时错误。
    • 如果T是数组类型,则发生编译时错误。
  • 如果S是数组类型SC [],即,数组类型为SC的组件:

[为简洁起见,省略]

假设我有动物课,而猫和狗课又有动物课。 猫,动物和狗都有方法speak(),用于猫打印“喵”,用于狗打印“ woof”和用于动物“不会说话”。

好了,所以这终于是我的问题了。 如果先让一只猫( c )然后运行Animal a = c;该怎么办? 如果我运行a.speak();会怎样? 哪个调用了speak()方法? 当我改变这样的类型时到底发生了什么? 我会有任何真正的理由使用它吗?

Java中的对象完全知道创建对象的类型。 它实际上是一个隐藏字段(您可以使用Object.getClass()方法进行检索)。 此外,所有非静态方法解析都从最特定类的方法定义开始,并朝着最通用类( Object )进行; 由于Java中实现的唯一继承,因此这是一个简单的搜索。 Cat知道这是Animal的子类型,这是Object的子类型,而c知道它是Cat ,与变量的类型无关。

进行分配时, 编译器会检查要分配的值的已知类型是分配给的类型还是其子类型之一 如果是这样,分配工作。 如果不是这样,您将需要一个显式的强制转换(在运行时进行适当的类型检查;强制转换不能破坏Java的类型系统,它们只能使其难看)。 它不会改变以下事实:方法查找仍然是动态完成的,并且对象仍然知道它实际上是什么类型。 该程序正在做的所有工作都在忽略一些信息。 如果您了解C ++,则可以将Java视为仅具有虚拟方法(和静态方法),并且将查询处理到vtable中非常简单,因为继承菱形或其他有害情况没有问题。

使用接口的实现时,几乎是一样的,只是执​​行了更复杂的查找(即,首先像以前一样先查找vtable中的索引,然后再进行操作)。 但是,拥有一个实现接口的对象意味着必须有一些可以完全实现该接口的类,到那时,这又都相对简单了。 记住,所有真正复杂的工作都是在编译时完成的。 在运行时,在所有情况下事情都是相对简单的。

那么,您会利用其中的任何一个吗? 好吧,您应该 (而且您实际上会发现在真实代码中很难避免)。 从接口或超类定义合同的角度考虑是一种很好的方式,其中子类型遵守合同,而调用者不必知道详细信息。 Java库非常频繁地使用它,特别是因为合同类型细节及其实现的可见性可能不同。 所有客户端代码都知道对象遵守合同,属于给定类型。

根据您的背景,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

据我了解,您使用接口来解耦代码。 您要针对接口而不是实现进行编程: “编程为接口”是什么意思? 您将使用抽象类来实现功能,这对于所有实现类都是微不足道的,因此您无需在所有实现类中都编写它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM