正在参考书籍练习...
具有以下代码。
Left left = createLeftInstance ();
Right right = createRightInstance ();
...并考虑到上述两种方法都可以返回Left和Right的所有子类的实例,因此在Java中调用以下方法...
left.invoke (right);
如何解决:
答案 0 :(得分:1)
A)是这里的正确答案。
下面的代码演示了这一点。
public class Main001 {
public static void main(String[] args) {
A right = createRightInstance();
B left = createLeftInstance();
left.invoke(right);
System.out.println("Done!!!");
}
public static B createLeftInstance() {
return new B2();
}
public static A createRightInstance() {
return new A2();
}
}
class A{
}
class A1 extends A{
}
class A2 extends A1{
}
class B{
public void invoke(A x) {
System.out.println("Invoking method A on B with argument " + x.getClass().getName());
}
public void invoke(A1 x) {
System.out.println("Invoking method A1 on B with argument " + x.getClass().getName());
}
public void invoke(A2 x) {
System.out.println("Invoking method A2 on B with argument " + x.getClass().getName());
}
}
class B1 extends B{
public void invoke(A x) {
System.out.println("Invoking method A on B1 with argument " + x.getClass().getName());
}
public void invoke(A1 x) {
System.out.println("Invoking method A1 on B1 with argument " + x.getClass().getName());
}
public void invoke(A2 x) {
System.out.println("Invoking method A2 on B1 with argument " + x.getClass().getName());
}
}
class B2 extends B1{
public void invoke(A x) {
System.out.println("Invoking method A on B2 with argument " + x.getClass().getName());
}
public void invoke(A1 x) {
System.out.println("Invoking method A1 on B2 with argument " + x.getClass().getName());
}
public void invoke(A2 x) {
System.out.println("Invoking method A2 on B2 with argument " + x.getClass().getName());
}
}
此示例打印
Invoking method A on B2 with argument A2
Done!!!
这意味着A)是正确的答案。
这是为什么呢?
好吧,因为:
1)调用B2类中的方法(如输出所示),并且B2是left
的运行时类型(left
的编译时类型是B)。
2)调用带有参数A的方法(请注意,A是right
的编译时类型),即使right
的运行时类型是A2。 right
的编译时类型只是声明right
的类型,即A。right
的运行时类型是参数的实际类型,即A2(参见输出,它表示{ {1}})。
答案 1 :(得分:1)
实际上,我认为技术上正确的答案是“以上皆非”。
在编译时,您需要了解left
变量(Left
)和right
变量(Right
)的声明类型。这将确定Left::invoke
方法的哪种重载方法 1 最适用于Right
类型的参数。
在运行时,left
的实际类型将确定调用哪个实际方法。
因此,完整的答案是:
E)基于
left
的编译时和运行时类型以及right
的编译时类型。
但是,我怀疑教科书中这个问题的重点是帮助您区分(简单)编译时方法解析和运行时方法分派。为此,A)“足够正确”。
1-要确定,编译器需要将Right
及其超类型与invoke
声明的Left
方法及其超类型的不同方法重载进行比较。如果存在多个重载,则编译器需要选择“最适用的”重载。
答案 2 :(得分:0)
Java具有 A ,称为单调度:
right
的编译时类型与left
的编译时类型提供的方法进行匹配)left
的运行时类型上-由于方法不会消失,left
当然具有与在编译时选择的签名相同的方法。此行为被视为“调度”,并且仅取决于left
(在运行时),因此为“单个”。使用内置的println(Object)
和println(char[])
的超级简单演示:
char c[]={'a','b','c'};
System.out.println(c);
Object o=c;
System.out.println(o);
results类似于
abc [C@1540e19d
第一行显示println(char[])
连接了字符数组,第二行显示了在编译时以println(o==c);
传递的完全相同的数组(可以添加Object
之类的某些支票) -time导致调用println(Object)
重载,而与运行时类型无关。
B 和 C 可能不存在。
当在运行时也使用参数的实际运行时类型选择了方法签名,并且在运行时调用所选方法时, D 被称为多次调度。 left
的类型。 Java默认情况下不支持该功能,可以使用反射来实现,这是一个单参数示例:
public static void trickyprintln(Object o) throws Exception {
System.out.getClass().getMethod("println",o.getClass()).invoke(System.out,o);
}
public static void main (String[] args) throws Exception {
char c[]={'a','b','c'};
trickyprintln(c);
Object o=c;
trickyprintln(o);
}
其中一个results
使用参数的运行时类型手动选择{p> asabc abc
println
。因此,如果有人真的需要Java,就可以这样做,但不会自动发生。