[英]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();
}
}
兩個編譯錯誤是
在public class C extends B
, No enclosing instance of type A is available due to some intermediate constructor invocation
, No enclosing instance of type A is available due to some intermediate constructor invocation
在C myC = myA.new C();
, AC cannot be resolved to a type
坦率地說,我認為概念是合理的:我想制作B的子類,這樣當我為A制作B時,我可以選擇使其具有B中的功能或C中的功能。
我不想要的四種解決方法/解決方案,以及為什么我不想要它們:
“解決方案:將C放入A中。” 我不想這樣,因為如果我不能修改A.java的代碼(有應用程序有這個限制)怎么辦? 如果A是另一個API的一部分怎么辦? 然后我必須為C創建一個新文件,就像我在這里所做的那樣。
“解決方案:將C放在擴展A的D類中。” 我不希望這樣,因為C只限於在類型D的實例上實例化。我想創建一個擴展B的類,可以在類型A的所有實例上實例化(有些應用程序需要這個)。 因此,我需要C不被其他類封閉,就像我在這里所做的那樣。
(作為問題編輯添加 - 請參閱JoshuaTaylor對代碼示例的回答)“解決方案:使B靜態。” 我不希望這樣,因為如果B中的功能需要訪問其封閉的A實例(有應用程序需要這個),該怎么辦? 因此,我需要B不是靜態的,就像我在這里所做的那樣。 (第二個問題編輯:您可以使B靜態並使其構造函數接受其封閉實例,將其保存在受保護的變量中以便在其子代中進行訪問,但這不如RealSkeptic接受的答案優雅)
刪除。 請參見底部的編輯。
所以,如果你的回答表明我做了上述其中一個,那么這不是這個問題的答案,即使它可能對其他人有用。
如果您的回答是“這只是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甚至如果它實際上沒有使用它。 myA
和myC
之間的關聯是myA
是關於B
的myC
的直接封閉實例 。 該術語取自JLS第8.1.3節 :
對於
C
每個超類S
本身是類或接口SO
的直接內部類,存在與i
相關聯的SO
的實例,稱為關於S
的i
的直接封閉實例 。 當通過顯式構造函數調用語句調用超類構造函數時,確定對象的直接封閉實例(如果有的話)(§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.