web-dev-qa-db-ja.com

Javaで静的なネストされたインターフェイスが使用されるのはなぜですか?

コードベースで静的なネストされたインターフェイスを見つけました。

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

これを見たことがありません。元の開発者は手の届かないところにいます。したがって、私はSOに尋ねなければなりません:

静的インターフェイスの背後にあるセマンティクスは何ですか? staticを削除すると、何が変わりますか?なぜこれを行うのでしょうか?

231
Mo.

上記の例のstaticキーワードは冗長であり(ネストされたインターフェイスは自動的に「静的」です)、セマンティクスに影響を与えずに削除できます。削除することをお勧めします。インターフェイスメソッドの「パブリック」およびインターフェイスフィールドの「パブリックファイナル」についても同様です。修飾子は冗長であり、ソースコードに混乱を招くだけです。

どちらにしても、開発者はFoo.Barという名前のインターフェイスを宣言するだけです。 FooにアクセスできないコードがFoo.Barにもアクセスできないことを除いて、外側のクラスとの関連付けはありません。 (ソースコードから-Fooがパッケージプライベートの場合でも、バイトコードまたはリフレクションはFoo.Barにアクセスできます!)

新しいトップレベル名を作成しないように、外部クラスからのみ使用されることが予想される場合、この方法でネストされたインターフェイスを作成することは受け入れられるスタイルです。例えば:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});
291
Jesse Glick

質問には答えましたが、ネストされたインターフェイスを使用する正当な理由の1つは、その機能が属するクラスに直接関連している場合です。これの良い例はListenerです。クラスFooがあり、他のクラスがそのイベントをリッスンできるようにしたい場合は、FooListenerという名前のインターフェイスを宣言できますが、これは大丈夫ですが、おそらく宣言する方が明確ですネストされたインターフェースであり、それらの他のクラスにFoo.Listenerを実装させます(ネストされたクラスFoo.Eventはこれと一緒に悪いことではありません)。

71
ColinD

メンバーインターフェイスは暗黙的に静的です。例の静的修飾子は、コードのセマンティクスを変更せずに削除できます。 Java言語仕様も参照してください 8.5.1。静的メンバー型宣言

14
Bas Leijdekkers

内部インターフェイスは、アクセスするために静的である必要があります。インターフェイスはクラスのインスタンスではなく、クラス自体に関連付けられているため、次のようにFoo.Barでアクセスされます。

public class Baz implements Foo.Bar {
   ...
}

ほとんどの場合、これは静的な内部クラスと違いはありません。

Jesseの答えは近いですが、内部インターフェイスが有用である理由を示すより良いコードがあると思います。読み進める前に、以下のコードをご覧ください。内部インターフェイスが便利な理由を見つけることができますか?答えは、クラスDoSomethingAlreadyを次のようにインスタンス化できるということです。 どれか AおよびCを実装するクラス。具体的なクラスの動物園だけではありません。もちろん、これはACが内部でなくても達成できますが、長い名前(AとCだけでなく)を連結し、他の組み合わせ(たとえば、AとB、CとBなど)でこれを行うと簡単に想像できます物事が制御不能になる方法を参照してください。ソースツリーをレビューする人々は、1つのクラスでのみ意味のあるインターフェイスに圧倒されることは言うまでもありません。 内部インターフェイスにより、カスタムタイプの構築が可能になり、カプセル化が改善されます。

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}
6
user1982892

ご質問に直接回答するには、Map.Entryをご覧ください。

Map.Entry

また、これは便利かもしれません

Static Nested Inerfacesブログエントリ

3
Henry B

1998年、Philip Wadlerは、静的インターフェイスと非静的インターフェイスの違いを提案しました。

私の知る限り、インターフェースを非静的にすることの唯一の違いは、非静的な内部クラスを含めることができることです。そのため、変更によって既存のJavaプログラムが無効になることはありません。

例えば、彼は Expression Problem の解決策を提案しました。これは、一方で「あなたの言語がどれだけ表現できるか」という表現と「あなたが表現しようとしている用語」としての表現との不一致です一方で、あなたの言語」。

静的および非静的なネストされたインターフェイスの違いの例は、 彼のサンプルコード で見ることができます。

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

彼の提案はJava 1.5.0では決して実現しませんでした。したがって、他のすべての答えは正しいです。静的および非静的のネストされたインターフェイスに違いはありません。

0
Pindatjuh

クラスFooをインターフェイスFooに変更する場合、上記の例の「public」キーワードも冗長になります。

別のインターフェイス内で定義されたインターフェイスは暗黙的にpublic static。になります

0
Danylo Volokh

通常、静的内部クラスが表示されます。静的内部クラスは、非静的クラスが参照できる包含クラスを参照できません。パッケージの衝突が発生している場合を除き(Fooと同じパッケージにBarという名前のインターフェイスが既に存在する場合)、それを独自のファイルにしたいと思います。また、FooとBarの間の論理的な接続を強制することも設計上の決定かもしれません。おそらく著者は、BarをFooでのみ使用することを意図していました(ただし、静的な内部インターフェイスはこれを強制せず、単なる論理接続です)

0
basszero