关于类变量的向上转换和向下转换有什么区别?
例如,在以下程序类中,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();
}
}
答案 0 :(得分:177)
向上转换正在转换为超类型,而向下转换则转换为子类型。始终允许向上转换,但向下转换涉及类型检查,可以抛出ClassCastException
。
在您的情况下,从Dog
到Animal
的演员阵容是向上投射,因为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)
向上转换:当我们想要将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();
如何工作的示例。
假设你有三只狗
警犬 - 警犬延伸宠物狗。
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
上调用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}}方法。
原则上:
UPCASTING - >隐藏
DOWNCASTING - >揭示
答案 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();