为什么在java中允许向下转换?

时间:2015-06-05 06:30:07

标签: java inheritance

class Animal{
  public void findAnimal(){
    System.out.println("Animal class");
  }
  public void sayBye(){
    System.out.println("Good bye");
  }
}

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

鉴于上述继承,可以理解Animal的参考可以指Dog的对象

Animal animal=new Dog();

由于Dog对象可以执行动物可以执行的所有操作,如上所述,Dog也有sayBye和findAnimal方法。

但是为什么允许将一个Animal对象向下转换为Dog对象,该对象没有用处并且在运行时失败。

Dog dog=(Dog)new Animal(); // fails at runtime but complies.

Dog dog=(Dog)animal;

上面的语句看起来很合理,因为动物引用指向Dog对象。

5 个答案:

答案 0 :(得分:5)

当您从外部代码获取超类的对象时,允许这种类型的转换,例如:作为方法的参数,然后需要调用特定于子类的方法。

这不是一个好习惯,但在一些罕见的情况下,你被迫做这样的事情,所以语言允许:

void sound(Animal animal) {
    if (animal instanceof Dog) {
        Dog dog = (Dog)animal();
        dog.bark();
    }
    if (animal instanceof Cat) {
        Cat cat = (Cat)animal();
        cat.meow();
    }
}
  

为什么允许编译Dog dog=(Dog) new Animal()

因为编译器设计者决定在编译时不检测此错误。他们验证了转换为Dog的表达式是Dog的超类,并允许表达式进行编译。他们可以更进一步,检查表达式是否总会导致异常,但这需要额外的努力才能在语言的用户体验方面获得很少的改进。

答案 1 :(得分:3)

因为你有时需要它。

特别是当Java还没有泛型(Java 1.4及更早版本)时,当你从一个集合中获取一个对象时,你几乎总是需要进行强制转换。

// No generics, you don't know what kinds of objects are in this list
List list = new ArrayList();

list.add(new Dog());

// Need to cast because the return type of list.get() is Object
Dog dog = (Dog)list.get(0);

由于我们从Java 5开始就有了泛型,因此大大减少了对转换的需求。

您应尽量避免在代码中进行转换。强制转换是一种故意关闭编译器类型检查的方法 - 通常你不想这样做,你想利用编译器的检查而不是绕过它。所以,如果你有你需要演员的代码,那就多想一想,看看你是否可以在没有演员的情况下编写代码。

答案 2 :(得分:1)

您需要该功能才能将早期的强制转换对象作为其原始类型进行访问。

例如,如果您将Dog转换为Animal以将其传递给通用处理器,您可能稍后需要将其强制转换为Dog以执行特定方法。

开发人员负责确保类型兼容 - 并且当它没有错误时。一些伪代码:

public void example(Animal foo){
    if( ...condition... ) ((Dog)foo).bark();
    else if( ...other condition... ) ((Cat)foo).meow();
}

自从引入泛型以来,这种情况不太常用,但仍然存在这种情况。如果您不想要错误,开发人员全权负责保证类型是正确的。

答案 3 :(得分:0)

案例1 -

这里我们使用松散耦合。

动物动物= getSomeDog(),

狗狗=(狗)动物; //这是允许的,因为动物可以引用一只狗

案例2

这里我们使用紧耦合。

动物动物=新动物();

狗狗=(狗)动物; //这会在运行时失败,因为动物没有引用狗

在运行时有可能成功的时候使用Downcast 所以案例1有可能在运行时超过案例2

答案 4 :(得分:0)

Down casting is considered as a bad Object Oriented practice. It must be avoided to as much extent as possible.

Java still has it and your question is a good question as why Java allows Down-casting.

Suppose a case below.

public interface List{
    public boolean add(Object e);
    public boolean remove(Object o);
}  

public class ArrayList implements List{

  // Extra method present in the ArrayList and not in the parent Interface
    public Object[] toArray() {
        // returns array of the objects
        return Arrays.copyOf(elementData, size);
    }

    @Override
    public boolean add(Object e){
        // add e to the ArrayList Underlying array
    }
    @Override
    public boolean remove(Object o){
       // remove o from the ArrayList Underlying array
    }
}

A good Object oriented practice is to Code for Interfaces. But often there are methods defined in the concrete implementations which we need to call. I read an comment from some one and I quote it in my words.

Know the Rules, in case you need to break them Do break them Knowingly and take care so as to prevent from any adverse effect.

Below is an example where we need to do the Down-casting. The example of down-casting in your question is to teach what is down-casting, below is real life example.

public void processList(List items){
   items.add( new Object() );
   items.add( new Object() );
   processAsPerTypeOfList(items);
}

public void processAsPerTypeOfList( List items ){
    if( items instanceof ArrayList){
         Object[] itemArray = ((ArrayList)items).toArray();// DOWNCASTING
         // Process itemArray
    }
} 

For more reference you can also see a related question : Why Java needs explicit downcasting?