簡體   English   中英

為什么外部類不能擴展內部類?

[英]Why can't outer classes extend inner classes?

為什么我不能這樣做/是否有解決方法來完成此任務:

package myPackage;

public class A {
    public class B {

    }
}

package myPackage;  

import myPackage.A.B;

public class C extends B {

}

package myPackage;

public class Main {
    public static void main(String[] args) {
        A myA = new A();
        C myC = myA.new C();
    }
}

兩個編譯錯誤是

  1. public class C extends BNo enclosing instance of type A is available due to some intermediate constructor invocationNo enclosing instance of type A is available due to some intermediate constructor invocation

  2. C myC = myA.new C(); AC cannot be resolved to a type

坦率地說,我認為概念是合理的:我想制作B的子類,這樣當我為A制作B時,我可以選擇使其具有B中的功能或C中的功能。

想要的四種解決方法/解決方案,以及為什么我不想要它們:

  1. “解決方案:將C放入A中。” 我不想這樣,因為如果我不能修改A.java的代碼(有應用程序有這個限制)怎么辦? 如果A是另一個API的一部分怎么辦? 然后我必須為C創建一個新文件,就像我在這里所做的那樣。

  2. “解決方案:將C放在擴展A的D類中。” 我不希望這樣,因為C只限於在類型D的實例上實例化。我想創建一個擴展B的類,可以在類型A的所有實例上實例化(有些應用程序需要這個)。 因此,我需要C不被其他類封閉,就像我在這里所做的那樣。

  3. (作為問題編輯添加 - 請參閱JoshuaTaylor對代碼示例的回答)“解決方案:使B靜態。” 我不希望這樣,因為如果B中的功能需要訪問其封閉的A實例(有應用程序需要這個),該怎么辦? 因此,我需要B不是靜態的,就像我在這里所做的那樣。 (第二個問題編輯:您可以使B靜態並使其構造函數接受其封閉實例,將其保存在受保護的變量中以便在其子代中進行訪問,但這不如RealSkeptic接受的答案優雅)

  4. 刪除。 請參見底部的編輯。

所以,如果你的回答表明我做了上述其中一個,那么這不是這個問題的答案,即使它可能對其他人有用。

如果您的回答是“這只是Java語言的一個缺陷,那么您根本無法實現這個概念性的想法”,這是一個好的答案,您應該發布它。 但是只是一個警告:如果您錯了,我會推遲將您的答案標記為已接受。 如果這是您的答案,我將非常感謝您解釋為什么對該語言的這種限制已經到位(因為這是該問題的標題)。

感謝您的幫助。

編輯:JoshuaTaylor的回答提出了一個有效的選項:你可以匿名擴展B,避免編寫構造函數,就像在RealSkeptic接受的答案中一樣。 我最初放棄了這個想法,因為它不允許你通過“A.this”訪問C的封閉A實例。 但是,我已經知道C沒有A的封閉實例,除非它在A的定義中作為嵌套類特別定義。 所以請注意:以下解決方案都沒有允許您通過在C方法中寫“A.this”來訪問包含C的祖先B的A的封閉實例。類只能使用“.this”來訪問它們的類型具體嵌套在。但是,如果B具有訪問A的封閉實例的功能,則需要通過JoshuaTaylor方法的匿名類或通過RealSkeptic方法的任何其他類。

好吧,它可以完成,但你必須記住,每個構造函數都需要顯式或隱式地調用它的超級構造函數。 這就是為什么你得到“由於某些中間構造函數調用而沒有A類的封閉實例”錯誤。 C的no-args構造函數試圖隱式調用B的無參數構造函數,如果沒有A ,它就不能這樣做。

所以你修復你的C是:

public class C extends B {
    public C(A enclosing) {
        enclosing.super();
    }
}

然后你可以使用以下方法創建一個新的C

A myA = new A();
C myC = new C(myA);

答案中的問題

  • @Andi特納問道:

    如果你明確地將A傳遞給C的構造函數,C現在不能是靜態的,並且在C中使用A作為“普通舊”成員變量來調用所需的方法嗎?

    應該注意的是,C既不是靜態的也不是內部的。 它是一個單獨的公共類,它擴展了一個內部類B.C的作者可能不知道B類的實現,所以它不知道使用A的方法是什么,也不能訪問任何私有成員A,因為C不是A的成員但是B確實如此,並且B需要A實例。 另一種方法是組合而不是繼承(其中C持有B實例並將操作委托給它),但如果它想要創建該B實例而不是將其傳遞到內部,它仍然需要一個A實例,盡管它會使用enclosing.new B而不是enclosing.super

  • @rajuGT問:

    C是個體嗎? 如果是這樣,為什么它需要A對象? 在這種情況下,myA和myC之間的聯系是什么?

    是的,C是一個單獨的實體。 對於任何自己的方法都不需要A. 但是如果它試圖調用(或繼承並且不覆蓋)B中涉及訪問A的方法 - 那么B的實現需要A。當然,任何B的實例都需要引用A甚至如果它實際上沒有使用它。 myAmyC之間的關聯是myA關於BmyC直接封閉實例 該術語取自JLS第8.1.3節

    對於C每個超類S本身是類或接口SO的直接內部類,存在與i相關聯的SO的實例,稱為關於Si直接封閉實例 當通過顯式構造函數調用語句調用超類構造函數時,確定對象的直接封閉實例(如果有的話)(§8.8.7.1)

官方參考此用法

這種用法稱為限定的超類構造函數調用語句 ,在JLS的8.8.7.1節 - 顯式構造函數調用中提到。

超類構造函數調用以關鍵字super (可能以顯式類型參數開頭)或Primary表達式或ExpressionName開頭。 它們用於調用直接超類的構造函數。 他們進一步分裂:

  • 非限定超類構造函數調用以關鍵字super開頭(可能以顯式類型參數開頭)。

  • 合格的超類構造函數調用以Primary表達式或ExpressionName開頭。 它們允許子類構造函數顯式指定新創建的對象關於直接超類(第8.1.3節 )的直接封閉實例。 當超類是內部類時,這可能是必要的。

在該部分的末尾,您可以找到顯式構造函數調用語句的示例,包括此用法。

您可以輕松擴展嵌套的靜態

更新:你提到,你不希望這第一個解決方案,但問題的措辭可能會導致人們把它誰願意有內部類是靜態的,所以我會在希望離開這個,這是非常有用的給他們。 對於您的確切問題,更恰當的答案在本答案的第二部分。

你可以,但內部類必須是靜態的 ,因為如果不是,那么內部類的每個實例都有對外部類的封閉實例的引用。 靜態嵌套類沒有該引用,您可以自由擴展它。

public class Outer {
    public static class Inner {

    }
}
public class InnerExtension extends Outer.Inner {

}

但您也可以擴展嵌套的非靜態類

package test;

public class Outer {
    public class Inner {
        public String getFoo() {
            return "original foo";
        }
    }
}
package test;

public class Extender {
    public static void main(String[] args) {
        // An instance of outer to work with
        Outer outer = new Outer();

        // An instance of Outer.Inner 
        Outer.Inner inner = outer.new Inner();

        // An instance of an anonymous *subclass* of Outer.Inner
        Outer.Inner innerExt = outer.new Inner() {
            @Override
            public String getFoo() {
                return "subclass foo";
            }
        };

        System.out.println("inner's class: "+inner.getClass());
        System.out.println("inner's foo: "+inner.getFoo());
        System.out.println();
        System.out.println("innerExt's class: "+innerExt.getClass());
        System.out.println("innerExt's foo: "+innerExt.getFoo());
    }
}
inner's class: class test.Outer$Inner
inner's foo: original foo

innerExt's class: class test.Extender$1
innerExt's foo: subclass foo

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM