繁体   English   中英

在Java中转换引用变量

[英]Casting reference variable in Java

关于在Java中转换引用变量,我有一些不清楚的地方。

我有两个类A和B. A是B的超类。如果我有两个对象,然后是print语句:

A a = new A(); //superclass

B b = new B(); //subclass

System.out.println ((A)b);

那么执行println方法到底发生了什么?

我知道因为B是A的子类,所以我可以进行以下演员:

A a2 = (A)b;

我也知道当println将引用变量作为参数时,会调用(隐式)调用创建了object-argument的类的toString()方法。 就是这样,因为方法println()正在寻找String类型的参数,而toString()方法将该对象表示为字符串。 即使我们不写toString(),也会隐式调用该方法。 因此,以下两个陈述是等效的:

System.out.println (b);

System.out.println (b.toString());

所以,我的问题是:我们采取的隐含行动是什么

System.out.println ((A)b); 

我想参考变量b的类型会自动从B变为A.变量应该仍然指向同一个对象 - 用

B b = new B();

但只是b的类型现在会改变。 这个对吗? 另一个问题:即使我已经将b的类型更改为超类的类型,是否会调用子类中的重写方法,而不是超类的方法?

非常感谢。

问候

演员在这种情况下没有影响。

System.out.println(XXX)接受不同类型的参数(多个重载版本),但在这种情况下,您将获得带有Object的版本。 由于Java中的每个对象都支持toString(),因此无论它是什么,都会在实际参数上调用toString。

现在,由于Java中的所有方法都是动态调度的,因此运行的版本是与动态类型对应的版本。 将B的对象转换为A只会更改表达式的静态(声明)类型。 动态类型(实际上是什么)仍然是B.因此,B中的版本被调用。

PrintStream类中有许多println(...)声明(这是System.out的类型)。

其中两个是:

void println(String x)
void println(Object x)

当你调用println((A)b) ,编译器选择调用println(Object)因为A不是String (或者println支持的任何其他类型)。 当你调用println(b.toString())编译器选择println(String) ,因为你正在传递一个字符串。

在您的情况下,将bA无效,因为println()没有A或B类型的声明。 但是转换仍然会发生(因为你要求它),或者它可能不会,因为编译器将它优化掉,因为它知道它是多余的并且它不会失败并且没有效果。

写这不是惯用语:

A a2 = (A)b;

因为这是多余的,因为B是A的子类。可能是编译器会优化掉转换(这是一个运行时操作来检查对象是否属于特定类型,永远不会改变它的类型)。

一旦构造了类型B的对象,它的类型永远不会改变。 它总是一个B:

class B extends/implements A {...}
B b = new B();   // construct a B
A a = b;         // assign a B to an A variable, it's superclass
A a = (A) b      // as above including check to see that b is an A (redundant, may be optimised away).

B b = a;         // Syntax error, won't compile
B b = (B) a      // Will check whether a is of type B then assign to variable b

在最后一种情况下,由于BA的子类,因此a可以保存B的实例并且转换将成功。 或者它可能是一个持有一个其他类的实例,它扩展/实现/是A而不是B ,你将得到一个ClassCastException

因此,由于类型B的对象始终保留它的标识(它是“B”--ness),因此调用该对象的任何(实例)方法将始终调用B的实现,而不管您访问该对象的变量是否被声明为A或B.

请记住,您只能调用在定义变量的类中声明的方法。

因此,例如,如果B声明了一个方法b_only()那么编译器将不允许你编写a.b_only() ; 你可以写((B)a).b_only()

我想当我们在java中使用引用变量时,通过使用这个变量,我们可以分配任何类类型的对象。 在大多数情况下,我们创建了Interface和抽象类的引用变量,因为我们无法创建接口和抽象类的对象,因此在接口或抽象类的引用变量中分配类的对象。 EX-

Interface X {
 public abstract void xx();
 public abstract void yy();
}

Class XXX implements X {
   ...........
}

Class XY extends XXX {
  X xy = new XXX();
} 

这里xy是接口X的引用,并在接口的引用中分配类XXX的对象。

所以根据我的观点,通过使用引用变量我们也可以使用接口来参与对象的创建。

这个对吗?

有点。 转换表达式的结果将是A类型。 'b'变量的类型将始终保持为B类型。

另一个问题:即使我已经将b的类型更改为超类的类型,是否会调用子类中的重写方法,而不是超类的方法?

将调用底层对象的实例方法。 例:

class Foo {
    public static void main(String[] args) {
        B b = new B();
        assert "B".equals(((A) b).m());
    }
}

class A {
    String m() { return "A"; }
}

class B extends A {
    String m() { return "B"; }
}

由于Java方法都具有动态分派,因此调用哪个函数不依赖于引用的静态类型。 因此,无论有没有演员,结果都是相同的。 [结果可能会有所不同,如果你是向下转换 - 转换版本可能会抛出异常]

始终将您的对象视为它实例化的类型(在您的情况下为B)。 如果它是向上倾向于将其视为 - 嗯 - 将其视为B穿上衣服。 它可能看起来像A,你可能无法做任何你想要做的漂亮B事,但在衣服里面它仍然是B - 衣服根本不会改变底层物体。

所以摘要是 - 你只能调用A中的方法,但是当你调用它时,它会直接执行并执行它,就像它是B一样。

问题:即使我已经将b的类型更改为超类的类型,是否会调用子类中的重写方法,而不是超类的方法?

在这种情况下,调用子类b的方法; 令人信服地理解为什么; 您可能涉及以下现实世界场景

考虑父类的父表现出行为(方法): 高度定义为

父亲很高;身高= 6'2“

儿子是继承父亲的高度行为的儿童阶层;因此他也很高; 高度为6'显然超越了行为

每当你的子类Son调用他名字上的行为高度时,他就会显示被覆盖的行为,即他自己的身高6'

如前所述,由于被覆盖的方法是动态绑定的,因此在这种情况下无法进行转换。 由于toString存在于所有对象中,因此它满足此条件,因此要在运行时确定要调用的对象类型和方法。

但请注意,所有方法都不是这种情况,因为只有被覆盖的方法是动态绑定的。 重载方法是静态绑定的。 这里的许多答案都提到java方法总是动态绑定,这是不正确的。

有关更详细的说明,请参阅此问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM