ネストされた列挙型はコンストラクタで親の静的メンバを参照するとき、なぜ私がNPEを得るのですか?

rymach:

(私の知る限りとして)再作成する条件:

  1. ネストされた列挙型は、親の静的メンバを参照します
  2. ネストされたクラス
  3. 親クラスの静的メンバは、入れ子になったクラスへのコンストラクタの引数として列挙型を取ります
  4. 列挙型は、親クラスで何かする前に、外部クラスによって参照されています

オンラインこのコードを実行します。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());
  }
}
ジム・ギャリソン:

ここで何が起こっているかです。

  1. 実行mainメソッドの開始
  2. あなたはを参照してください。 Recreate.Car.TESLA
  3. クラスローダがロードおよび初期化するために開始しますenum Car以下に述べたように、クラスがRecreateまだロードまたは初期化されていません。
  4. 以下のためには、初期化子TESLAを指し、FEATURES
  5. これは、クラスを引き起こしRecreateロードおよび初期化されます
  6. 静的初期化の一環としてRecreate、クラスGarage、ロードintialized、およびインスタンスがされてONE_CAR_GARAGE作成されます。

ここでの問題は、この時点での建設がということですenum Car完了していない、とCar.TESLA値を持ちますnull

クラスが入れ子にすることができるにもかかわらず、ネストされたクラスがロードされ、外側のクラスの初期化の一部として初期化されるとは限りません。彼らはソースに入れ子に見えるかもしれないが、それぞれ、すべてのクラスが独立しています。静的な入れ子になったクラスはトップレベルのクラスに相当します。非静的クラスも同じですが、隠された参照を経由して含んでいるクラスのメンバを参照する能力を持っています。

あなたは、デバッガでこれを実行する場合、いくつかの場所でのブレークポイントを入れて、自分の目で確かめてください、そして各ブレークポイントでスタックを調べることができます。

Iは、試験/指示設定したブレークポイントで、次のコードで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());
    }   
}

あなたがヒットする最初のブレークポイントは、1つになります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これは、あなたがクラス間に作成した複雑な依存関係によって決まるので、解決策は、依存関係を解くになっています。どのようにあなたがそれを行うことはあなたの究極の目標に依存します。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=186865&siteId=1