繁体   English   中英

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

[英]left and right objects are evaluated / resolved during runtime or compile-time?

参考书籍练习......

有以下代码..

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

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

left.invoke (right);

怎么解决:

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

实际上,我认为技术上正确的答案是“以上都不是”。

  • 在编译时,您需要知道left变量( Left )和right变量( Right )的声明类型。 这将确定Left::invoke方法的哪个方法重载1最适用于Right类型的参数。

  • 在运行时, left的实际类型将决定调用哪个实际方法。

所以完整的答案是:

E)基于编译时AND运行时类型的leftright编译时类型。

但是,我怀疑教科书中这个问题的关键是帮助您区分非重载方法的编译时解析和运行时方法调度。 为此目的,A)“足够正确”。


1 - 为了进行确定,编译器需要将Right及其超类型与Left及其超类型声明的invoke方法的不同方法重载进行比较。 如果存在多个重载,则编译器需要选择“最具体的适用”重载。

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(参见输出,它with argument A2表示)。

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);

导致类似的东西

 abc [C@1540e19d 

第一行显示println(char[])连接字符数组,第二行显示完全相同的数组(可以添加一些检查,如println(o==c); )在编译时结果中作为Object传递在调用println(Object)重载时,无论运行时类型如何。

BC可能不存在。

当在运行时使用参数的实际运行时类型选择方法签名时, 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);
}

导致

 abc abc 

因为println是使用参数的运行时类型手动选取的。 因此,如果有人真的需要它在Java中,它可以做,但它不会自动发生。

暂无
暂无

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

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