ChatGPT解决这个技术问题 Extra ChatGPT

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

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

例如在下面的程序中,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();
    }
}
DogAnimal。大多数时候向上转换是不必要的,除非你想使用某种重载方法。 callme 存在于 AnimalDog 中。 callme2 仅存在于 Dog 中,您将 a 转换为 Dog 以使其工作。
你的代码的输出是什么?
有趣的是,d.callme 返回 'In callme of Dog' 尽管 d 被强制转换为动物!!
@Chris311 'd' 和 'a' 都指向同一个对象......这是一个 Dog,但 'a' 在运行时向下转换时只能访问 Dog 特定的方法。事实:动物 a = (动物)d;是不必要的,你只需要 Animal a = d;正如你在向上吹。

a
awksp

向上转换是转换为超类型,而向下转换是转换为子类型。向上转型始终是允许的,但向下转型涉及类型检查并可能抛出 ClassCastException

在您的情况下,从 DogAnimal 的转换是向上转换,因为 Dog is-a Animal。通常,只要两个类之间存在 is-a 关系,您就可以向上转换。

向下转换将是这样的:

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

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

但是,如果您要这样做:

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

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

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

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

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

从引入 pattern matching for instanceof 的 Java 16 开始,向下转型可以更简洁地表达:

Animal animal = getAnimal(); // Maybe a Dog? Maybe a Cat? Maybe an Animal?
if (animal instanceof Dog castedDog) {
    // now castedDog is available here as in the example above
}

适当的向下转换是否确保没有 ClassCastException?像第一种情况一样?
@MS“正确”是什么意思?
@awksp 这是一个出色而清晰的答案。几乎总结了我需要了解的有关 Casting 的所有信息。
你肯定没有做过动物是狗的例子的课程吧?那你为什么还要检查呢?
P
Premraj

https://i.stack.imgur.com/Lkn0S.png

Upcasting:当我们想将 Sub 类转换为 Super 类时,我们使用 Upcasting(或加宽)。它自动发生,无需明确执行任何操作。

Downcasting :当我们想将 Super 类转换为 Sub 类时,我们使用 Downcasting(或缩小),而 Downcasting 在 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

Autoboxing-vs-Casting


那么,没有这样一种向上转换的方式来调用方法的父级吗?
C
Community

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

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

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

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

假设你有三只狗

Dog - 这是超级类。宠物狗 - 宠物狗扩展了狗。警犬 - 警犬延伸宠物狗。公共类狗 { 公共字符串 getType () { System.out.println("NormalDog");返回“普通狗”; } } /** * Pet Dog 有一个额外的方法 dogName() */ public class PetDog extends Dog{ public String getType () { System.out.println("PetDog");返回“宠物狗”; } public String dogName () { System.out.println("我没有名字!!");返回“没有名字”; } } /** * Police Dog 有一个额外的方法 secretId() */ public class PoliceDog extends PetDog{ public String secretId() { System.out.println("ID");返回“身份证”; } public String getType () { System.out.println("我是警犬");返回“警犬”; } }

多态性: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

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

当您尝试在 obj3 上调用 secretID(); 方法时,它是 PoliceDog object,但引用了 Dog,它是层次结构中的一个超类,它会引发错误,因为 obj3 无权访问 secretId() 方法.为了调用该方法,您需要手动将该 obj3 向下转换为 PoliceDog

  ( (PoliceDog)obj3).secretID();

打印 ID

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

  ( (PetDog)obj2).dogName();

为什么会这样,向上转换是自动的,但向下转换必须是手动的?嗯,你看,向上转型永远不会失败。但是,如果您有一组不同的 Dog 并且想要将它们全部向下转换为它们的类型,那么有可能这些 Dog 中的一些实际上是不同的类型,即 PetDogPoliceDog,并且过程失败,通过抛出 ClassCastException

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

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


“多态性在方法调用期间使用自动向下转换。”不,它没有。没有指定使用的机制,但最常用的机制 - 一个 vtable - 没有这样的事情。查看目标代码。没有沮丧。
为什么不?这就是发生的事情,你能举一个它不起作用的例子吗?
为什么不?这就是正确的情况......你能举一个例子,说明“多态性在方法调用期间使用自动向下转换”。会失败还是不会成立?
这是你的争论。由你来证明。显示在目标代码中向下转换发生的位置。 “为什么不”这个问题的答案是“因为没有必要”。 vtable 负责方法调度,并且变量已经指向整个对象。
“据我所知,我的陈述是真实的,并且在任何情况下都成立”不是证据。这只是一个断言。我要求你证明你的陈述。你不这样做。实际上,您只是在重复自己。我已经提供了几个反驳。我还提供了一个决策程序。如果您可以在方法调用的目标代码中找到向下转换,那么您是对的,我错了。科学就是这样完成的。去做吧。并声称我“大胆地依赖于文件”是一种公然的失实陈述。不要那样做。
A
Andrejs

我知道这个问题很久以前就问过了,但是对于这个问题的新用户。请阅读这篇文章,其中包含关于上转型、下转型和使用 instanceof 运算符的完整描述

无需手动向上转换,它会自行发生: Mammal m = (Mammal)new Cat();等于哺乳动物 m = new Cat();

但是必须始终手动进行向下转换: Cat c1 = new Cat();动物 a = c1; //自动向上转换为动物猫 c2 = (Cat) a; //手动向下转换回猫

为什么会这样,向上转换是自动的,但向下转换必须是手动的?嗯,你看,向上转型永远不会失败。但是,如果您有一组不同的动物,并且想将它们全部转换为猫,那么这些动物中的一些实际上是狗,并且通过抛出 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


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

最好尝试这种方法进行向上转换,它很容易理解:

/* 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() 方法访问。
c
cbr

也许这张表有帮助。调用类 Parent 或类 Childcallme() 方法。作为一个原则:

UPCASTING --> 隐藏 DOWNCASTING --> 揭示

https://i.stack.imgur.com/BwnWF.jpg

https://i.stack.imgur.com/jbEYr.jpg

https://i.stack.imgur.com/qokUb.jpg


C
Community

1.- 向上转型。

进行向上转换时,您定义了某种类型的标记,该标记指向子类型的对象(类型和子类型可以称为类和子类,如果您觉得更舒服的话......)。

Animal animalCat = new Cat();

这意味着这样的标签,animalCat,将只具有 Animal 类型的功能(方法),因为我们已经将它声明为 Animal 类型,而不是 Cat 类型。

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

2.- 垂头丧气。

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

由于我们已经创建了指向 Cat 对象的 animalCat 标记,我们需要一种方法来调用 Cat 对象的方法,从我们的 animalCat 标记中以某种智能漂亮的方式调用。

这样的过程就是我们所说的向下转换,我们只能在运行时进行。

一些代码的时间:

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

}

a
amit shah

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

===== 向上转换:- 方法:对象 c1 将引用类的方法(Figo - 方法必须被覆盖),因为类“Figo”是用“new”指定的。实例变量:对象 c1 将引用声明类(“汽车”)的实例变量。

当声明类是父类并且对象是由子类创建时,就会发生隐式转换,即“向上转换”。

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

====== 使用:当对象是子类并且声明类是父类并且子类想要访问它自己的类而不是父类的实例变量时,可以使用“向下转换”来完成。


D
David

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

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

因此,写

Animal a = (Animal)d;

或者

Animal a = d;

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

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

在您的示例中,您无法从 UseAnimlas 访问 Animalcallme() 代码(因为 Dog 会覆盖它),除非方法如下:

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

J
Jérémie B

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

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