簡體   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