在运行时或编译时评估/解析左对象和右对象?

时间:2019-05-31 22:45:44

标签: java polymorphism

正在参考书籍练习...

具有以下代码。

Left left = createLeftInstance ();
Right right = createRightInstance ();

...并考虑到上述两种方法都可以返回Left和Right的所有子类的实例,因此在Java中调用以下方法...

left.invoke (right);

如何解决:

  • A)基于left的运行时类型和right的编译时间
  • B)基于left的编译时类型和right的运行时
  • C)基于左侧的编译时类型和右侧的编译时
  • D)基于左侧的运行时类型和右侧的运行时

3 个答案:

答案 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

abc
abc
使用参数的运行时类型手动选择{p> as println。因此,如果有人真的需要Java,就可以这样做,但不会自动发生。