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


138

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

例如,在下面的程序类中,动物仅包含一个方法,而狗类包含两个方法,然后将我们如何将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();
    }
}

A Dog是一个Animal。在大多数情况下,除非您想使用某种重载的方法,否则不需要进行向上转换。和callme中都存在。仅存在于中,您对其进行了强制转换以使其起作用。AnimalDogcallme2DogaDog
布莱恩(Brian)

您的代码输出是什么?
Malwinder Singh 2015年

有趣的是,尽管d被浇铸到动物上,但d.callme返回“在狗的呼号中”!
克里斯311年

4
@ Chris311'd'和'a'都指向同一个对象...这是一条Dog,但是当'a'在运行时向下转换时,只能访问特定于Dog的方法。事实:动物a =(动物)d;是不必要的,只需要Animal a = d; 当您up丧时。
马克·基恩

Answers:


223

向下转换将转换为超类型,而向下转换将转换为子类型。始终允许进行向上转换,但是向下转换涉及类型检查,并且可能引发ClassCastException

在您的情况下,从a Dog到a的转换Animal是不正确的,因为Dogis -a Animal。通常,只要两个类之间存在is-a关系,就可以进行转换。

向下转换将是这样的:

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

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

但是,如果要这样做:

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

你会得到一个ClassCastException。原因是因为animal的运行时类型为Animal,所以当您告诉运行时执行强制类型转换时,它会发现animal实际上不是a Dog,因此抛出a ClassCastException

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

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

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;
}

适当的向下转换是否可以确保没有ClassCastException?喜欢第一种情况?
Malwinder Singh 2015年

@MS“适当”是什么意思?
awksp 2015年

2
@awksp这是一个出色而清晰的答案。几乎总结了我需要了解的有关Casting的所有信息。
Gautham Honnavara '16

当然,您还没有上过动物是狗的实例的课程吗?那你为什么还要检查呢?
barlop

62

向下广播和向上广播如下:
在此处输入图片说明

Upcasting:当我们要将Sub类转换为Super类时,我们使用Upcasting(或扩展)。它会自动发生,无需显式执行任何操作。

Downcasting:当我们要将Super类转换为Sub类时,我们使用Downcasting(或缩小),并且在Java中不能直接进行Downcasting,这是我们必须要做的。

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

因此,没有这样的方法来进行向上转换以调用方法的父级吗?
karlihnos

32

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

向上转换和向下转换与将原语相互转换不一样,我相信当程序员开始学习转换对象时,这会引起很多混乱。

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

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

假设你有三只狗

  1. 狗-这是超级班。

  2. 宠物狗-宠物狗扩展了Dog。

  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";
     }
    }

多态:默认情况下,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,由于无法访问方法,将引发错误。为了调用该方法,您需要将该obj3手动向下转换为PoliceDog objectDogobj3secretId() PoliceDog

  ( (PoliceDog)obj3).secretID();

哪个打印 ID

以类似的方式调用类中的dogName();方法,PetDog您需要向下转换obj2PetDog因为obj2被引用Dog并且无权访问dogName();方法

  ( (PetDog)obj2).dogName();

为什么会这样,向上转换是自动的,但是向下转换必须是手动的?好吧,您知道,向上转换永远不会失败。但是,如果你有一组不同的狗,并希望他们都垂头丧气到它们的类型,然后有机会的话,其中一些狗实际上是不同类型的,即,PetDogPoliceDog,和过程失败,抛出ClassCastException

这就是如果您已将对象引用到超类类型时,需要手动向下转换对象的原因。

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


“多态性在方法调用期间使用自动向下转换。” 不,不是。没有指定使用的机制,但是最常用的机制-vtable-却没有这样做。查看目标代码。没有沮丧。
洛恩侯爵

为什么不?这是怎么回事,您能举个无法解决的例子吗?
Nagarjuna Yelisetty 2015年

1
为什么不?这就是正确的事情..您能举个例子吗?“多态性在方法调用期间使用自动向下转换”。会失败还是会失败?
Nagarjuna Yelisetty 2015年

这是你的争论。由您来证明。显示下调发生在目标代码中的何处。问题“为什么不”的答案是“因为没有必要”。vtable负责方法的分派,并且变量已经指向整个对象。
洛恩侯爵

1
“据我所知,我的陈述是正确的,并且在任何情况下都适用”,这并不是证明。这仅仅是一个断言。我要证明你的陈述。你没有这样做。实际上,您只是在重复自己。我已经提出了几项驳斥。我还提供了决策程序。如果您可以在目标代码中找到方法调用的缩水,那么您是对的,我是错的。这就是科学的过程。做吧 声称我“大胆地依赖于文档”是一种公然的虚假陈述。不要那样做
洛恩侯爵,2015年

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”,该功能测试对象是否是某个Class的实例。

 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;
    }

有关更多信息,请阅读本文


优点:哺乳动物m =(哺乳动物)new Cat();等于哺乳动物m = new Cat();
Catbuilts

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();
    }
}

最后一行可能是:((Dog)ref).callme2(); //用于Dog类的向下转换/缩小和callme2()方法访问。
udarH3

6

也许这张桌子有帮助。调用callme()class Parent或class 的方法Child。作为原则:

升级->隐藏

下载->显示

在此处输入图片说明

在此处输入图片说明

在此处输入图片说明


4

1.-向上投射。

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

Animal animalCat = new Cat();

这意味着该标签animalCat仅具有Animal类型的功能(方法),因为我们已将其声明为Animal类型,而不是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();
        
    }

}

2

父级:汽车
子级:Figo
Car c1 = new Figo();

=====上
播:-
方法:对象c1将引用类的方法(Figo- 必须重写方法),因为用“ new”指定了“ Figo”类。
实例变量:对象c1将引用声明类(“ Car”)的实例变量。

如果声明类是父类,并且创建了子对象,则进行隐式转换,即“ Upcasting”。

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

======
使用:当Object是子类,而声明类是Parent,而子类想要访问它自己的类而不是父类的Instance变量时,可以使用“向下转换”完成。


1

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

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

因此,写作

Animal a = (Animal)d;

要么

Animal a = d;

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

相反,向下转换是必需的,因为您已将其定义a为Animal的对象。目前,您知道它是Dog,但是java不能保证它是。实际上,在运行时可能会有所不同,java会抛出ClassCastException,这会发生。当然,您的示例示例并非如此。如果您不愿意a使用Animal,则Java甚至无法编译应用程序,因为Animal它没有方法callme2()

在您的示例中callme(),除非方法如下,否则无法访问Animalfrom 的代码UseAnimlas(因为将其Dog覆盖):

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

0

我们可以创建向下转换的对象。在这种类型也。:调用基类方法

Animal a=new Dog();
a.callme();
((Dog)a).callme2();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.