关于类变量的向上转换和向下转换有什么区别

时间:2014-05-01 18:30:48

标签: java casting class-variables downcast upcasting

关于类变量的向上转换和向下转换有什么区别?

例如,在以下程序类中,Animal只包含一个方法但Dog类包含两个方法,那么我们如何将Dog变量转换为Animal变量。

如果完成了施法,那么我们如何使用Animal的变量调用Dog的另一种方法。

class Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}


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

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class UseAnimlas 
{
    public static void main (String [] args) 
    {
        Dog d = new Dog();      
        Animal a = (Animal)d;
        d.callme();
        a.callme();
        ((Dog) a).callme2();
    }
}

10 个答案:

答案 0 :(得分:177)

向上转换正在转换为超类型,而向下转换则转换为子类型。始终允许向上转换,但向下转换涉及类型检查,可以抛出ClassCastException

在您的情况下,从DogAnimal的演员阵容是向上投射,因为Dog是-Animal。通常,只要两个类之间存在is-a关系,就可以进行upcast。

向下倾斜会是这样的:

Animal animal = new Dog();
Dog castedDog = (Dog) animal;

基本上你正在做的是告诉编译器你知道对象的运行时类型是什么。编译器将允许转换,但仍将插入运行时完整性检查以确保转换有意义。在这种情况下,可以进行强制转换,因为在运行时animal实际上是Dog,即使animal的静态类型是Animal

但是,如果你这样做:

Animal animal = new Animal();
Dog notADog = (Dog) animal;

你得到ClassCastException。原因是因为animal的运行时类型是Animal,所以当你告诉运行时执行强制转换时,它会发现animal并不是真的Dog然后抛出ClassCastException

要调用超类的方法,您可以super.method()或执行上传。

要调用子类的方法,您必须进行向下转换。如上所示,您通常会冒ClassCastException这样做;但是,您可以使用instanceof运算符在执行强制转换之前检查对象的运行时类型,这样可以阻止ClassCastException s:

Animal animal = getAnimal(); // Maybe a Dog? Maybe a Cat? Maybe an Animal?
if (animal instanceof Dog) {
    // Guaranteed to succeed, barring classloader shenanigans
    Dog castedDog = (Dog) animal;
}

答案 1 :(得分:47)

向下和向上铸造如下:
enter image description here

向上转换:当我们想要将Sub类转换为Super类时,我们使用Upcasting(或扩展)。它会自动发生,不需要明确地做任何事情。

向下转换:当我们想要将Super类强制转换为Sub类时,我们会使用 向下转换(或缩小)和向下转换在Java中是不可能直接实现的,显然我们必须这样做。

Dog d = new Dog();
Animal a = (Animal) d; //Explicitly you have done upcasting. Actually no need, we can directly type cast like Animal a = d; compiler now treat Dog as Animal but still it is Dog even after upcasting
d.callme();
a.callme(); // It calls Dog's method even though we use Animal reference.
((Dog) a).callme2(); // Downcasting: Compiler does know Animal it is, In order to use Dog methods, we have to do typecast explicitly.
// Internally if it is not a Dog object it throws ClassCastException

答案 2 :(得分:29)

向上转换和向下转换是Java的重要组成部分,它允许我们使用简单的语法构建复杂的程序,并为我们提供了很多优势,例如多态或分组不同的对象。 Java允许将子类类型的对象视为任何超类类型的对象。这称为向上转播。上传是自动完成的,而向下转发必须由程序员手动完成 ,我会尽力解释为什么会如此。

向上转换和向下转换并不像将原语从一个转换为另一个,我相信当程序员开始学习构建对象时会导致很多混乱。

多态性:默认情况下,java中的所有方法都是虚拟的。这意味着在继承中使用任何方法都可以被覆盖,除非该方法被声明为final或static

根据对象(狗,宠物,警犬)类型,您可以在下面看到getType();如何工作的示例。

假设你有三只狗

  1. 狗 - 这是超级班。
  2. 宠物狗 - 宠物狗延伸狗。
  3. 警犬 - 警犬延伸宠物狗。

    public class Dog{ 
       public String getType () {
          System.out.println("NormalDog");
          return "NormalDog";
       }
     }
    
    /**
     * Pet Dog has an extra method dogName()
     */   
    public class PetDog extends Dog{ 
       public String getType () {
          System.out.println("PetDog");
          return "PetDog";
       }
       public String dogName () {
          System.out.println("I don't have Name !!");
          return "NO Name";
       }
     }
    
    /**
     * Police Dog has an extra method secretId()
     */
    public class PoliceDog extends PetDog{
    
     public String secretId() {
        System.out.println("ID");
        return "ID";
     }
    
     public String getType () {
         System.out.println("I am a Police Dog");
         return "Police Dog";
     }
    }
    
  4. 多态性:默认情况下,java中的所有方法都是虚拟的。这意味着在继承中使用任何方法都可以被覆盖,除非该方法被声明为final或static。(解释属于虚拟表概念)

      

    虚拟表/调度表:对象的调度表将包含对象的动态绑定方法的地址。通过从对象的调度表中获取方法的地址来执行方法调用。对于属于同一类的所有对象,调度表是相同的,因此通常在它们之间共享。

    public static void main (String[] args) {
          /**
           * Creating the different objects with super class Reference
           */
         Dog obj1 = new Dog();
    `         /**
               *  Object of Pet Dog is created with Dog Reference since                
               *  Upcasting is done automatically for us we don't have to worry about it 
               *  
               */
         Dog obj2 = new PetDog();
    `         /**
               *  Object of Police Dog is created with Dog Reference since                
               *  Upcasting is done automatically for us we don't have to worry       
               *  about it here even though we are extending PoliceDog with PetDog 
               *  since PetDog is extending Dog Java automatically upcast for us 
               */
          Dog obj3 = new PoliceDog();
    }
    
    
    
     obj1.getType();
    

    打印Normal Dog

      obj2.getType();
    

    打印Pet Dog

     obj3.getType();
    

    打印Police Dog

    需要由程序员手动完成向下转型

    当您尝试在secretID(); obj3上调用PoliceDog object方法时,Dog但是引用obj3这是层次结构中的超类,它会抛出错误secretId() 1}}无法访问PoliceDog方法。 要调用该方法,您需要手动将obj3转发 {{1} }

      ( (PoliceDog)obj3).secretID();
    

    打印ID

    以类似的方式调用dogName();课程中的PetDog方法,您需要将obj2转发给PetDog,因为obj2被Dog引用并且#39; t可以访问dogName();方法

      ( (PetDog)obj2).dogName();
    

    为什么如此,这种向上转换是自动的,但是向下转换必须是手动的?嗯,你看,向上转播永远不会失败。 但是如果你有一群不同的狗并希望将它们全部归结为它们的类型,那么就有机会,其中一些狗实际上是不同的类型,即PetDog,{{1通过抛出PoliceDog来处理失败。

    如果您已将对象引用到超类类型,则需要 手动向下转换对象

      

    注意:此处通过引用意味着当您向下转换它时,您不会更改对象的内存地址,它仍然保持不变,您只是将它们分组为特定类型ClassCastException

答案 3 :(得分:12)

我很久以前就知道这个问题,但对于这个问题的新用户。 请阅读此文章,其中包含有关 instanceof 运算符的上传,下转和使用的完整说明

  • 不需要手动上传,它可以单独发生:

    Mammal m = (Mammal)new Cat();等于Mammal m = new Cat();

  • 但必须始终手动完成向下转型:

    Cat c1 = new Cat();      
    Animal a = c1;      //automatic upcasting to Animal
    Cat c2 = (Cat) a;    //manual downcasting back to a Cat
    

为什么如此,这种向上转换是自动的,但是向下转换必须是手动的?嗯,你看,向上转播永远不会失败。但是如果你有一组不同的动物并希望将它们全部转移到猫身上,那么就有机会,这些动物中的一些实际上是狗,并且通过抛出ClassCastException来处理失败。 这是应该引入一个名为" instanceof" 的有用功能,它测试对象是否是某个类的实例。

 Cat c1 = new Cat();         
    Animal a = c1;       //upcasting to Animal
    if(a instanceof Cat){ // testing if the Animal is a Cat
        System.out.println("It's a Cat! Now i can safely downcast it to a Cat, without a fear of failure.");        
        Cat c2 = (Cat)a;
    }

有关详细信息,请参阅this article

答案 4 :(得分:6)

最好尝试这种方法进行上传,这很容易理解:

/* upcasting problem */
class Animal
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}

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

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class Useanimlas 
{
    public static void main (String [] args) 
    {
        Animal animal = new Animal ();
        Dog dog = new Dog();
        Animal ref;
        ref = animal;
        ref.callme();
        ref = dog;
        ref.callme();
    }
}

答案 5 :(得分:6)

也许这张桌子有帮助。 调用类callme()或类Parent的{​​{1}}方法。 原则上:

  

UPCAST​​ING - >隐藏

     

DOWNCASTING - >揭示

enter image description here

enter image description here

enter image description here

答案 6 :(得分:3)

1.-向上投射。

进行向上转换,您定义某种类型的标记,该标记指向子类型的对象(如果您觉得更舒服,则可以将Type和subtype称为class和subclass。)

Animal animalCat = new Cat();

这意味着动物标签只具有动物类型的功能(方法),因为我们已将其声明为动物类型,而不是猫类型。

我们被允许在编译时或运行时以“自然/隐式/自动”的方式进行操作,这主要是因为Cat继承了Animal的某些功能;例如,move()。 (至少猫是动物,不是吗?)

2.-下垂。

但是,如果我们需要从动物标签类型中获得Cat的功能,会发生什么?

当我们创建了指向Cat对象的animalCat标签时,我们需要一种方法来以一种聪明的方式从我们的animalCat标签中调用Cat对象方法。

这种过程称为“向下转换”,我们只能在运行时进行。

输入一些代码的时间:

public class Animal {
    public String move() {
        return "Going to somewhere";
    }
}

public class Cat extends Animal{
    public String makeNoise() {
        return "Meow!";
    }   
}

public class Test {

    public static void main(String[] args) {

    //1.- Upcasting 
    //  __Type_____tag________object
        Animal animalCat = new Cat();
    //Some animal movement
        System.out.println(animalCat.move());
        //prints "Going to somewhere"

    //2.- Downcasting   
    //Now you wanna make some Animal noise.
        //First of all: type Animal hasn't any makeNoise() functionality.
        //But Cat can do it!. I wanna be an Animal Cat now!!

        //___________________Downcast__tag_____ Cat's method
        String animalNoise = ( (Cat) animalCat ).makeNoise();

        System.out.println(animalNoise);
        //Prints "Meow!", as cats usually done.

    //3.- An Animal may be a Cat, but a Dog or a Rhinoceros too.
        //All of them have their own noises and own functionalities.
        //Uncomment below and read the error in the console:

    //  __Type_____tag________object
        //Cat catAnimal = new Animal();

    }

}

答案 7 :(得分:2)

父母:车 孩子:菲戈
Car c1 = new Figo();

=====
上溯造型: -
方法:对象c1将引用类的方法(Figo - Method必须被覆盖),因为类“Figo”被指定为“new”。
实例变量:对象c1将引用声明类(“Car”)的实例变量。

当Declaration类是parent并且对象是child创建的时,则会发生隐式转换,即“Upcasting”。

======
向下转换: -
Figo f1 =(Figo)c1; //
方法:对象f1将引用类的方法(figo),因为初始对象c1是使用类“Figo”创建的。但是一旦完成向下铸造,仅出现在“Figo”类中的方法也可以用变量f1来表示 实例变量:对象f1不会引用对象c1的Declaration类的实例变量(c1的声明类是CAR),但是使用向下转换它将引用类Figo的实例变量。

======
使用:当Object是Child Class并且声明类是Parent和Child类想要访问它自己的类的Instance变量而不是父类时,可以使用“Downcasting”来完成。

答案 8 :(得分:1)

upcasting意味着将对象转换为超类型,而向下转换则意味着转换为子类型。

在java中,不需要向上转换,因为它是自动完成的。它通常被称为隐式铸造。您可以指定它以使其他人清楚。

因此,写作

Animal a = (Animal)d;

Animal a = d;

导致完全相同的点,并且在两种情况下都将从callme()执行Dog

转发是必要的,因为您将a定义为Animal的对象。目前你知道它是Dog,但java并不能保证它。实际上在运行时它可能是不同的,java会抛出ClassCastException,这会发生。当然,您的示例示例并非如此。如果你不会将a强制转换为Animal,那么java甚至无法编译应用程序,因为Animal没有方法callme2()。< / p>

在您的示例中,您无法从callme()获取Animal的{​​{1}}代码(因为UseAnimlas会覆盖它),除非方法如下:

Dog

答案 9 :(得分:0)

我们可以为Downcasting创建对象。在这种类型也。 :调用基类方法

Animal a=new Dog();
a.callme();
((Dog)a).callme2();