![](/img/trans.png)
[英]Why do I get NPE when I invoke android.content.Context.getApplicationContext() within Application's constructor?
[英]Why do I get an NPE when a nested Enum references a parent static member in its constructor?
重建的條件(據我所知):
在線運行此代碼: https : //repl.it/repls/PlushWorthlessNetworking
import java.util.ArrayList;
class Recreate {
private static ArrayList FEATURES = new ArrayList();
public enum Car {
TESLA(FEATURES);
Car(ArrayList l) { }
}
public static class Garage {
final Car car;
Garage(Car car) {
this.car = car;
}
}
public static Garage ONE_CAR_GARAGE = new Garage(Car.TESLA);
}
class Main {
public static void main(String[] args) {
// inclusion of this line causes the next line to NPE
System.out.println(Recreate.Car.TESLA);
System.out.println(Recreate.ONE_CAR_GARAGE.car.toString());
}
}
以下是發生的事情:
Recreate.Car.TESLA
enum Car
。 如下所述,尚未加載或初始化類Recreate
。 TESLA
的初始化程序指的是FEATURES
Recreate
Recreate
,類Garage
被加載,intialized,並且實例ONE_CAR_GARAGE
被創建。 這里的問題是,此時, enum Car
的構造不完整, Car.TESLA
的值為null
。
盡管類可以嵌套,但嵌套類不是作為外部類初始化的一部分加載和初始化的情況。 它們可能看起來嵌套在源中,但每個類都是獨立的。 靜態嵌套類等同於頂級類。 非靜態類也是相同的,但是能夠通過隱藏引用引用包含類中的成員。
您可以自己查看是否在調試器中運行此命令,將斷點放在多個位置,並檢查每個斷點處的堆棧。
我使用以下代碼在Eclipse中對此進行了測試/調試,並在指定的位置設置了斷點。 它與您的代碼略有不同,但不應表現得不同:
public class Foo5
{
static class Recreate {
private static ArrayList FEATURES = new ArrayList();
public enum Car {
TESLA(FEATURES);
Car(ArrayList l) {
System.out.println("car"); // *** Breakpoint ***
}
}
public static Garage ONE_CAR_GARAGE = new Garage(Car.TESLA);
public static class Garage {
final Car car;
Garage(Car car) {
this.car = car; // *** Breakpoint ***
}
}
}
public static void main(String[] args) throws Exception {
Recreate.Car car = Recreate.Car.TESLA;
System.out.println(Recreate.Car.TESLA);
System.out.println(Recreate.ONE_CAR_GARAGE.car.toString());
}
}
您將要擊中的第一個斷點將是Garage(Car car)
構造函數中的斷點。 在那一點上檢查堆棧你會看到
Foo5$Recreate$Garage.<init>(Foo5$Recreate$Car) line: 23
Foo5$Recreate.<clinit>() line: 17
Foo5$Recreate$Car.<clinit>() line: 12
Foo5.main(String[]) line: 29
因此,當調用Garage
構造函數時,它尚未從創建Car
返回。 這是由您在類之間創建的復雜依賴關系決定的,因此解決方案是解開依賴關系。 你如何做到這將取決於你的最終目標。
你有一個隱藏的循環依賴,令JVM感到困惑。 我們來看看你的代碼吧。
class Recreate {
private static ArrayList FEATURES = new ArrayList();
public enum Car {
TESLA(FEATURES);
Car(ArrayList l) { }
}
public static class Garage {
final Car car;
Garage(Car car) {
this.car = car;
}
}
public static Garage ONE_CAR_GARAGE = new Garage(Car.TESLA);
}
我們還需要一些來自JLS頁面的片段。
類或接口類型T將在第一次出現以下任何一個之前立即初始化:
- 使用由T聲明的靜態字段,該字段不是常量變量(第4.12.4節)。
12.4.2。 詳細的初始化程序
...
- 接下來,按文本順序執行類的類變量初始值設定項和類的靜態初始值設定項,或接口的字段初始值設定項,就好像它們是單個塊一樣。
因此,我們的靜態數據在首次引用時正在初始化。 現在,你的Car.TESLA
是隱式static final
,但根據定義 ,它不是常數。
常量變量是基本類型或類型String的最終變量,使用常量表達式初始化
所以對於我們來說,也有在這里打球三個靜態非恆定的變量: FEATURES
, TESLA
和ONE_CAR_GARAGE
。
現在,在您的工作案例中,您引用Recreate.ONE_CAR_GARAGE
。 這是一個靜態字段的引用Recreate
,所以FEATURES
,然后ONE_CAR_GARAGE
得到初始化。 然后,初始化過程中 ONE_CAR_GARAGE
, TESLA
被因為它的枚舉類中引用初始化。 一切都很好。
但是,如果我們過早地引用枚舉,那么我們會以錯誤的順序執行操作。 Recreate.Car.TESLA
被引用,因此TESLA
被初始化。 TESLA
引用FEATURES
,所以Recreate
已被初始化。 這將導致FEATURES
和ONE_CAR_GARAGE
之前得到初始化TESLA
完成現有的。
這是隱藏的依賴性,讓你絆倒。 Recreate.Car
取決於Recreate
取決於Recreate.Car
。 將ONE_CAR_GARAGE
字段移動到Garage
類將導致它無法使用FEATURES
初始化並將解決您的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.