为什么Java需要明确的向下转换?

我已经看到了类似问题的其他答案,但所有这些答案都依赖于语言被定义为这样的事实。 以下是我正在寻求解释:

在inheritance层次结构中,父类型可以隐式保存子对象(为什么?),但是对于保存父对象的子引用,显式向下转换是必要的(为什么?)。

请举一些例子来解释为什么不这样做会失败,我的意思是使用动物,狗类型等。如果这个问题已经回答了我错过了它,引用它也会有所帮助。

例如:

class Animal{ public void eat(){}; } class Dog extends Animal{ public void bark(){}; } class App{ Animal an = new Dog(); //1. why can animal store a dog but not a dog store an animal Dog dog = (Dog) new Animal(); //2. Dog has the behavior of an Animal so why explicit downcast } 

我只是想知道这些行是如何理解的,而不仅仅是知道它们是语言语义。 如果你的答案看起来像一个奶奶向她的孙子解释这个,那就更好了。

编辑:我只是想知道DoginheritanceAnimal并拥有所有行为。 因此,如果没有明确的向下转换,应允许使用上面的数字2。

或者,那个(我想我现在得到了)当我要求Dog存放Animal ,有可能我实际上得到了CowHorse ,因为Animal是父母可以拥有它的任何子类型。 如果是这种情况,那么为什么Java允许Animal保持子类型,因为可能存在类似于Dog将要发生的子类型行为bark() ,因为编译器必须检查并报告。 我知道规则只是试图从最简单的意义上推理出来。

Java中严格类型绑定的好处是,如果可能,您将获得编译时错误,而不是运行时错误。

例:

 class Animal { void eat() {} } class Dog extends Animal { void bark() {} } class Pigeon extends Animal { void pick() {} } class MyFunction { void run() { Animal first = new Pigeon(); // the following line will compile, but not run ((Dog)first).bark(); } } 

如果您在这样的简单示例中有这样的代码,您将立即发现问题。 但是考虑在项目中存在这样的问题,在数百个类中,在数千行代码的深度上很少被称为函数。 在生产的一天,代码失败,您的客户感到不安。 并由您来找出它失败的原因,发生了什么以及如何解决它。 这是一项艰巨的任务。

所以,有了这个有点复杂的表示法,Java会让你再次思考你的代码,下一个例子就是它如何做得更好:

 class MyFunction { void run() { Pigeon first = new Pigeon(); // the following line will NOT compile first.bark(); // and neither will this. Because a Pigeon is not a Dog. ((Dog)first).bark(); } } 

现在你立即看到你的问题了。 这段代码不会运行。 您可以正确使用它来避免前面的问题。

如果你使你的Animalabstract (你应该),你会看到你只能实例化特定的动物,而不是一般动物。 之后,您将在需要时开始使用特定的,并且在使用通用类时可以重复使用某些代码。

背景

从概念上讲,运行时错误更难以查找和调试,然后编译时错误。 喜欢,认真努力。 (在Stack Overflow上搜索NullPointerException,你会看到数百人努力修复运行时exception)

在一个层次结构的事物中(一般来说,不是与编程有关),你可以拥有一些普遍的“那就是动物”。 你也可以有一些特定的东西“那是一只狗”。 当有人谈论一般事情时,你不能指望知道具体的事情。 动物不能吠树,因为鸟不能,猫也不能。

因此,特别是在Java中,原始程序员发现明智的做法是确定需要知道一个足够的对象来调用该对象的函数。 这可以确保如果您没有注意,编译器将警告您,而不是运行时。

你的具体情况

你假设:

 Dog dog = (Dog) new Animal(); 

应该工作,因为狗是动物。 但它不会,因为不是所有的动物都是狗。

但:

 Animal an = new Dog(); 

有效,因为所有的狗都是动物。 在这个特定的情况下

 Animal an = new Dog(); Dog dog = (Dog)an; 

也将工作,因为该动物的特定运行时状态恰好是狗。 现在,如果你改变它

 Animal an = new Pigeon(); Dog dog = (Dog)an; 

它仍然会编译,但它不会运行,因为第二行Dog dog = (Dog)an; 失败。 你不能把鸽子送给狗。

因此,在这种情况下,您将获得ClassCastException 。 如果您尝试将new Animal()强制转换为Dog,则相同。 动物不是狗。 现在,这将在运行时发生,这很糟糕。 在Java思维方式中,编译时错误比运行时错误更好。

是。 理解该理论的一个简单的实时例子是

每个卡车司机都是司机,但不是你不能说每个司机都是卡车司机。

哪里

 Driver -Parent Truck Driver - Child. Animal an = new Dog(); //1. why can animal store a dog but not a dog store an animal 

你订购了一只动物,店主为你送了一只狗,你很开心,因为狗是动物。

 Dog do = (Dog) new Animal(); //2. Dog has the behavior of an Animal so why explicit downcast 

你正在要求一只狗,而店主给你一只动物。 所以很明显你检查动物是不是狗。 不是吗? 或者你只是假设你有一只狗?

认为。

在java中使用了引用。 假设我们有以下课程

 class A{ Integer a1; public A(){ // a1 is initialized } public void baseFeature(){ super.baseFeature(); // some extra code } } 

 class B extends A{ Integer b1; Integer b2; public B(){ super(); // b1 , b2 are initialized } @Override public void baseFeature(){ super.baseFeature(); // some extra code } public void extraFeature(){ // some new features not in base class } } 

以下所有3个陈述均有效

 A a = new A(); B b = new B(); A b1 = new B(); 

在java中,引用用于引用Heap中保存的对象。

  1. 类型A的引用不应被视为它不能保存比A类对象具有更多内存所需的对象。它是引用而不是对象持有者。
  2. 在子类型对象创建的情况下,构造函数调用如下:父构造函数调用后跟对其正在创建对象的实际类的构造函数调用。
  3. 子类对象可以说具有至少与父类型一样多的特征。
  4. A b = new B()没有混淆,因为B的对象具有其父级的所有特征。 子类对象具有其父对象中定义的所有特性,因此可以在子类的对象上调用任何父类方法
  5. 子类可以有更多的function,父类没有,所以在父对象上调用子类的方法会导致问题

假设在Java B a = new A()是有效的,那么如果调用a.extraFeature() ,那么很明显是错误。

这是由编译时错误阻止的。 如果需要向下转换,那么程序员必须格外小心。 这是编译时错误的意图。 下面的案例下载不会导致问题,但程序员有责任看看情况是否属于这种情况。

 public void acceptChild(Child c){ c.extraMethodNotWithParent(); } Parent p = new Child(); acceptChild((Child)p); 

如果未完成向下转换,程序员将获得编译时警告。 程序员可以查看并查看实际对象是否真的属于子类类型,然后他/她可以进行显式向下转换。 因此,只有在程序员没有注意的情况下才会出现问题。

这里的所有回复都有帮助。 我很困惑。 去除我脑中的所有链条,如果我只是想,它现在看起来像:
父母可以持有子类型。
你的意思是多态性的好处及其实现方式。
子类型可以保留父类,但需要显式向下转换。
由于允许多态性,这种垂头丧气是一种保障。 要求编译器相信代码中的Animal实际上将是Dog类型想要持有的Dog实例。

这是因为inheritance是特化,当你inheritance类T ,子类ST ,因为Java不需要强制转换,但是T可以有很多子, S'inheritanceTS' ,所以如果你有一个S的对象并且它被引用为T并且你想再次获得你的原型类型,那么你应该对S进行强制转换,并在运行时检查类型。

一个例子:男人和女人都是人,但人是男人或女人。

 Man man = new Man(); Human human = man //Legal. Man man2 = (Man) humain; //Legal Woman human = (Woman) humain; //Error, Java can't cast the humain to Woman, because its declared originaly as Man. 

如果Java确实是实现了强制转换,那么它会报告它并且不会在你的位置决定,这个检查是在运行时进行的。