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


235

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

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

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

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


2
これは「内部インターフェース」ではなく、ネストされたインターフェースです。Javaでは、Innerに特定の意味があります。
ローン侯爵

回答:


293

上記の例の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() {...}
});

1
Jesse Glickの答えでは、これはどういう意味ですか(Fooがパッケージプライベートでも、バイトコードまたはリフレクションはFoo.Barにアクセスできます!)。
Vasu

Kaillash、プライベートメソッドには、relection(reflectパッケージ内)を介して、および生成された.classファイルのバイトコードに直接アクセスしてアクセスできます。
gmoore

2
「バイトコード... Foo.Barにアクセスできます」とは、Fooを参照できなかった場合でも、Foo.Barを参照するコンパイル済みクラスをロードして実行できることを意味します。これは、Fooがパブリックであったときに以前にクラスがコンパイルされた場合、またはクラスが手動でアセンブルされた場合、またはJava以外の言語からコンパイルされた場合などに発生する可能性があります。結果のバイトコードがその包含クラスを参照しない場合でも。
Jesse Glick

@Jesseリフレクションを通じてプライベートトップレベルクラスのプライベート静的クラスにアクセスできますか?
Pacerier 2012年

1
@Pacerier:いいえ。より正確には、クラスをロードしてそのメンバーを検査できますが、インスタンス化したり、setAccessible(true)を使用せずにメソッドを呼び出したりすることはできません。つまり、リフレクションを介して表示されますが、アクセスできません。ただし、ネストされた静的クラスがパブリックの場合、静的に(コンパイル中に)アクセスできなくても、デフォルトでリフレクションを介してアクセスできます。
Jesse Glick

72

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


11
典型的な例はjava.util.Map.Entry(別のインターフェースにネストされたインターフェースです)です。
パウロEbermann

4
これは古いトピックであることはわかっていますが、外部クラスを独自のパッケージに含め、補足インターフェイス(などMap.Entry)またはクラスをそのパッケージ内に含めることをお勧めします。これは、クラスを短く簡潔にしたいからです。また、リーダーは、パッケージ内のクラスを確認することで、クラスに関連する他のエンティティを確認できます。おそらくjava.collections.map地図用のパッケージがあるでしょう。これはオブジェクト指向とモジュール性についてです。java.utilそれが多すぎます。utilのようなものcommon-においIMO
David Kerr

1
@Shaggy:java.util確かに多すぎます。そうは言っても、あなたが提案しているような細かいパッケージに分解するのも理想的だとは思いません。
ColinD 2012年

1
@DavidKerrこのインターフェースをjava.util.MapEntry独自のパッケージの外部で呼び出すとします。最初の反射:このクラスに関連するインターフェースは何ですか?私はクラスを調べます。javadocがこのインターフェースにリンクしていない限り、私はそれらの関係の手がかりを知りません。さらに、人々はインポートパッケージを見ません。誰もが自分の意見を持っています。だれも正しくない、だれも間違っていない
Raymond

@RaymondChenon私が言ったことの一部は、Mapとそれに関連するクラス、たとえばMap.Entryをjava.collections.mapなどの別のパッケージに入れることでした。これにより、マップに関連するすべてのコードが1つの場所(パッケージ)にあり、トップレベルのMapクラスに負担がかかりません。関連する実装(HashMapなど)を同じパッケージに入れることもできます。
David Kerr

14

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


これは「内部インターフェース」ではなく、ネストされたインターフェースです。Javaでは、Innerに特定の意味があります。
ローン侯爵

@EJP、私は同義語を使用してさまざまなブログを読みました。内部インターフェースはありますか?それらはネストされたインターフェースとどう違うのですか?
Number945

@BreakingBenjamin、ネストされたインターフェースは静的を意味します。内部クラスとネストされたクラスが存在しますが、内部インターフェイスはありません。その場合でも、ネストされたインターフェースとして呼び出す必要があります。内部-非静的およびネストは静的です。
Sundar Rajan

9

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

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

ほとんどの点で、これは静的内部クラスと同じです。


35
ネストされたインターフェイスは、キーワードを記述したかどうかにかかわらず、自動的に静的です。
パウロEbermann

3
コミュニティが別の回答を受け入れるために投票する方法が本当に必要です:stackoverflow.com/a/74400/632951
Pacerier


@ ClintonN.Dreisbach The interface isn't associated with instances of the class, but with the class itselfさらに意味を説明できますか、理解できませんでした
Kasun Siyambalapitiya '07 / 07/17

6

Jesseの答えは近いですが、内部インターフェースが役立つ理由を示すより良いコードがあると思います。先に進む前に、以下のコードを確認してください。内部インターフェースが役立つ理由を見つけることができますか?その答えは、DoSomethingAlreadyクラスは、AおよびCを実装する任意のクラスでインスタンス化できるということです。具体的なクラスのZooだけではありません。もちろん、これは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;
  }
}

2
これはナンセンスです。クラスZooはインターフェースを実装していないACため、のインスタンスを期待するZooコンストラクターに渡すことはできません。実際に両方を拡張し、そして、クラスが実装することを意味するものではありませんし、実装も魔法のように。DoSomethingAlreadyACACACACAC
Holger

3

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

Map.Entry

これも役に立つかもしれません

Static Nested Inerfacesブログエントリ


4
これは一例ですが、実際の答えではありません。
パウロEbermann

Map.Entryを使用して、「ペア」オブジェクトをたくさん作成しています。露出しています。Pairの実装には2つの考え方がありますが、それはここでは重要ではありません。Map.Entryは内部にあるかもしれませんが、外部で使用しています。
Ravindranath Akila 14

0

通常、静的内部クラスが表示されます。静的内部クラスは、非静的クラスが参照できる包含クラスを参照できません。あなたがいくつかのパッケージの衝突に遭遇していない限り(Fooと同じパッケージにBarと呼ばれるインターフェースがすでにあります)、私はそれをそれ自身のファイルにすると思います。また、FooとBarの間の論理的な接続を強制する設計上の決定となる場合もあります。おそらく、作成者はBarをFooでのみ使用することを意図していました(ただし、静的内部インターフェースはこれを強制せず、論理接続のみです)。


「静的インナー」は言葉で矛盾しています。ネストされたクラスは、いずれかの静的または内部。
ローン侯爵

0

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

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


質問に答えませんでした
Raining

0

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

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

たとえば、彼は表現問題の解決策を提案しました。これは、一方では「あなたの言語はどれだけ表現できるか」という表現と、他方では「あなたの言語で表現しようとしている用語」という表現との間のミスマッチです。 。

静的と非静的の入れ子になったインターフェイスの違いの例は、彼のサンプルコードで確認できます

// 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では実現しませんでした。したがって、他のすべての答えは正しいです。静的および非静的な入れ子になったインターフェイスに違いはありません。


これはすべて、Javaのジェネリックの非常に初期のプロセッサであったGJを指すことに注意してください。
ローン侯爵

-1

Javaでは、静的インターフェース/クラスにより、インターフェース/クラスをトップレベルのクラスのように使用できます。つまり、他のクラスで宣言できます。したがって、次のことができます。

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

staticがない場合、上記はコンパイルに失敗します。これの利点は、インターフェイスを宣言するためだけに新しいソースファイルを必要としないことです。また、Foo.Barを記述する必要があるため、インターフェイスBarをクラスFooに視覚的に関連付け、FooクラスがFoo.Barのインスタンスで何かを行うことを意味します。

Javaのクラス型の説明


staticなしでコンパイルます。「静的」は冗長です。
ローンの侯爵

@EJP:私がリンクしたその記事によると、静的なしでは内部クラスは所有クラスでのみ使用でき、所有クラスの外部では使用できません。staticはネストされたトップレベルのクラスになり、所有するクラスのスコープ外のオブジェクトで使用できます(少なくとも記事によると)。staticは完全に冗長ではなく、内部クラスのスコープに影響します。これはクラスに適用され、インターフェースは常にトップレベルのオブジェクトのように機能する場合があります(チェックする言語仕様を読み取る必要があります)。多分私は私の答えのインターフェースとクラスの部分を分離するべきでした(私の悪い)。
Skizz、2015

-6

静的とは、パッケージ(プロジェクト)のクラス部分がポインタを使用せずにアクセスできることを意味します。これは状況に応じて役立ちます。

「静的」メソッドの便利な例は、Mathクラスです。Mathのすべてのメソッドは静的です。つまり、邪魔にならないで新しいインスタンスを作成し、変数を宣言してさらに多くの変数に格納する必要がなく、データを入力して結果を取得するだけで済みます。

Staticは常に便利であるとは限りません。たとえば、大文字と小文字を比較する場合は、いくつかの方法でデータを保存することができます。同じシグネチャを持つ3つの静的メソッドを作成することはできません。静的でない3つのインスタンスが必要です。静的な場合は、データが入力とともに変化しないため、比較して比較できます。

静的メソッドは、1回限りのリターンと迅速な計算または簡単に取得できるデータに適しています。


1
静的の意味を知っています。私は今までこれまでインターフェイスでそれを見たことがありません。したがって、あなたの質問は、オフトピックです-申し訳ありません
ミズーリ

1
彼の答えは話題外だと思う。
Koray Tugay 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.