Java中的向上转型

3
在Java中,假设我有三个类,C继承自B,B继承自A。
class X {
   interface A {}
   interface B extends A {}
   interface C extends B {}
   void f(A a) {}

   void test() {
      C c = new C()
      B b = (B) c;

      f(b);
   }
}

如果我像上面的test()函数一样做这样的事情:
C c = new C()
B b = (B) c;

f(b);

f()接受b作为类型C,因为CB都继承自A。我希望f()接收b作为类型B而不是类型C

有没有办法强制进行向上转型?

9个回答

9

f()总是接收到一个被标记为A的类型(即使在底层它实际上是B或C,并且可以适当地向下转换)。

你可以定义一个额外的f(),如下所示:

f(B b);

如果必要的话

f(C c);

根据参数的类别,正确的函数将被调用。也就是说,编译器根据参数类型确定调用哪个函数。这与运行时发生的动态分派(或多态)不同。

请注意,您在问题中的强制转换是多余的。您可以写成:

C c = new C()
B b = c;

f(b);

因为C继承自B,所以C 是一个 B。


2
你似乎对编译时类型和运行时类型的区别感到困惑。
你正在创建一个类型为C的对象(由引用c和b指向),它将始终是C,因为无法更改对象的运行时类型和行为。通过强制转换,你只能更改其编译时类型,这会影响编译器处理它的方式。
你能否提供有关你试图解决的具体问题的更多信息?最有可能的是通过更改设计来实现你的目标。

是的,我可以通过从C创建B的新实例来完成我想要的操作,但我想了解为什么我不能首先尝试的方式来完成 :) 谢谢。 - simao

1
我想让f以B类型而不是C类型接收b。
C是B,B是A。
在f内部,f只看到其参数a的A部分。如果f调用一个在C中被覆盖的公共A函数,则会调用C的覆盖函数。
这就是虚函数的工作原理。其思想是,所引用的对象实际上是C,因此它应该表现出C的行为。如果您想要B的行为,请传递B实例,而不是C实例。
如果C和B应该具有相同的行为,请勿在C中覆盖该行为。
你为什么想这样做?

这正是我想要理解的。我希望参数表现出B的行为。我想我必须在传递给f之前从C创建一个新的B实例。谢谢。 - simao
如果你想让f在B上运行,为什么不直接创建一个B呢?C从哪里出现? - tpdi

1

你的问题不太清楚。你说的“f接受b作为类型C”是什么意思?

f接受b作为类型A,因为方法签名中写的是“A”。如果你在b上调用方法,它们将在C上调用,如果C覆盖了它们的话。但这是Java的标准行为(所有方法都像C++中的虚函数),没有办法改变它。

也许你可以描述一下你实际的问题,这样我们或许能够帮助你。


我希望函数f能够根据参数的类型调用相应的方法,所以如果参数是C类型,我想调用C.deliverTo()方法,但如果参数是B类型,我想调用B.deliverTo()方法。谢谢。 - simao

1

你的问题可以通过理解 REFERENCE 类型和 INSTANCE 类型之间的区别来解决。当你将 C 对象强制转换为 B 时,你只改变了引用类型 - 对象的实际实例仍然是 C 对象 - 如果你在调试模式下查看对象,这应该是非常明显的。改变引用类型只影响编译器处理/感知代码的方式 - 运行时行为不应受影响。任何对 C 对象的方法调用都将始终调用 C 的方法(无论对象是否已转换为其他类型)。如果你重写了 B 的方法并想要调用 B 版本的方法,则需要创建一个 B 实例。


0

你可以使用反射来检查参数是否为A类:

public void f(A a) {
  if (a.getClass() == A.class) {
    // ok, go on
  }
  else {
    System.err.println("The parameter is not of class A!");
  }
}

不知道那是否是你想要的,但它可能会有所帮助。


0
In upcasting and downcasting the  object first upcast then downcastenter
class A
{
}
class B extends A
{
}
Class M
{
  psvm(String ar[])
{

   A a1= new B();
   B b2=(B)a1;
}

0

在Java中,您可以将A的任何子类作为f(A a)的参数传递,这是面向对象编程的固有特性,无法绕过。如果C扩展了A,则您始终可以在期望A的位置使用它。


0

你的代码引用了一个A并不意味着被指向的对象就是A。如果它是C,那么它仍然是C。源代码中的引用只限制了源代码中可用的方法。强制转换只是因为有时候我们需要在不同类型上使用某个方法,我们需要欺骗编译器让它允许我们开始使用强制转换后的类型上的方法。自然地,如果尝试无效,则强制转换可能在运行时失败。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接