96

この質問に答えるために Java 言語仕様を検索しているときに、私は次のことを知りました。

クラスを初期化する前に、その直接のスーパークラスを初期化する必要がありますが、クラスによって実装されるインターフェイスは初期化されません。同様に、インターフェイスのスーパーインターフェイスは、インターフェイスが初期化される前に初期化されません。

好奇心で試してみたところ、予想通り、インターフェースInterfaceTypeは初期化されませんでした。

public class Example {
    public static void main(String[] args) throws Exception {
        InterfaceType foo = new InterfaceTypeImpl();
        foo.method();
    }
}

class InterfaceTypeImpl implements InterfaceType {
    @Override
    public void method() {
        System.out.println("implemented method");
    }
}

class ClassInitializer {
    static {
        System.out.println("static initializer");
    }
}

interface InterfaceType {
    public static final ClassInitializer init = new ClassInitializer();

    public void method();
}

このプログラムは印刷します

implemented method

ただし、インターフェイスでdefaultメソッドが宣言されている場合は、初期化が行われます。InterfaceType次のように与えられたインターフェースを考慮してください

interface InterfaceType {
    public static final ClassInitializer init = new ClassInitializer();

    public default void method() {
        System.out.println("default method");
    }
}

次に、上記の同じプログラムが印刷されます

static initializer  
implemented method

つまり、staticインターフェイスのフィールドが初期化され (詳細な初期化手順のステップ 9 )、static初期化される型の初期化子が実行されます。これは、インターフェースが初期化されたことを意味します。

これが起こるべきであることを示すものを JLS で見つけることができませんでした。誤解しないでください、実装クラスがメソッドの実装を提供しない場合にこれが発生する必要があることは理解していますが、そうである場合はどうなりますか? この条件が Java 言語仕様に欠けているのでしょうか、何か抜けているのでしょうか、それとも間違って解釈しているのでしょうか?

4

4 に答える 4

13

InterfaceType.init非定数値 (メソッド呼び出し) によって初期化されている定数フィールドがどこにも使用されていないため、インターフェイスは初期化されません。

インターフェイスの定数フィールドはどこでも使用されず、インターフェイスにはデフォルトのメソッドが含まれていないことがコンパイル時にわかっているため (Java-8 の場合)、インターフェイスを初期化またはロードする必要はありません。

インターフェイスは次の場合に初期化されます。

  • コードで定数フィールドが使用されています。
  • インターフェイスにはデフォルトのメソッドが含まれています (Java 8)

Default Methodsの場合は、あなたが実装していInterfaceTypeます。したがって、InterfaceTypeデフォルトのメソッドが含まれている場合は、実装クラスで継承 (使用)されます。そして初期化が絵になります。

ただし、(通常の方法で初期化される) インターフェイスの定数フィールドにアクセスする場合は、インターフェイスの初期化は必要ありません。

次のコードを検討してください。

public class Example {
    public static void main(String[] args) throws Exception {
        InterfaceType foo = new InterfaceTypeImpl();
        System.out.println(InterfaceType.init);
        foo.method();
    }
}

class InterfaceTypeImpl implements InterfaceType {
    @Override
    public void method() {
        System.out.println("implemented method");
    }
}

class ClassInitializer {
    static {
        System.out.println("static initializer");
    }
}

interface InterfaceType {
    public static final ClassInitializer init = new ClassInitializer();

    public void method();
}

上記の場合、フィールドを使用しているため、 Interface が初期化されてロードされますInterfaceType.init

あなたの質問ですでにそれを示しているので、デフォルトのメソッドの例は示していません。

Java 言語の仕様と例はJLS 12.4.1に記載されています (例にはデフォルトのメソッドは含まれていません)。


デフォルト メソッドの JLS が見つかりません。2 つの可能性があります。

  • Java の人々は、デフォルト メソッドのケースを考慮するのを忘れていました。(仕様書のバグ。)
  • デフォルトのメソッドをインターフェイスの非定数メンバーとして参照するだけです。(しかし、どこにも言及されていません。仕様ドキュメントのバグです。)
于 2014-04-16T06:08:33.663 に答える
1

インターフェースの初期化が、サブタイプが依存するサイドチャネルの副作用を引き起こしてはならないというケースを作成しようとします。したがって、これがバグであるかどうか、または Java が修正する方法が何であれ、それは重要ではありません。インターフェイスが初期化されるアプリケーション。

の場合、classサブクラスが依存する副作用を引き起こす可能性があることは広く受け入れられています。例えば

class Foo{
    static{
        Bank.deposit($1000);
...

のサブクラスはFoo、サブクラス コードのどこかに、銀行に $1000 が表示されることを期待します。したがって、スーパークラスはサブクラスの前に初期化されます。

スーパーインターフェースについても同じことをすべきではないでしょうか? 残念ながら、スーパーインターフェースの順序は重要ではないと想定されているため、それらを初期化する明確な順序はありません。

したがって、インターフェイスの初期化でこの種の副作用を確立しない方がよいでしょう。結局のところ、interface利便性のために積み上げたこれらの機能 (静的フィールド/メソッド) を意図したものではありません。

したがって、その原則に従えば、インターフェイスがどの順序で初期化されるかは問題になりません。

于 2014-04-22T22:38:06.280 に答える