web-dev-qa-db-ja.com

インターフェースとクラスの内部クラス

これらの2つの内部クラス宣言の違いは何ですか?メリット/デメリットについてもコメントしてください。

ケースA:クラス内のクラス。

public class Levels {   
  static public class Items {
    public String value;
    public String path;

    public String getValue() {
      return value;}
  }
}

ケースB:インターフェース内のクラス。

public interface Levels{

  public class Items {
    public String value;
    public String path;

    public String getValue() {
      return value;}
  }
}

Getvalueメソッドの配置を修正しました。

詳細情報:インターフェイスA AT ALLを実装していない別のクラスでAとBの両方の場合にItemsクラスをインスタンス化できます。

public class Z{//NOTE: NO INTERFACE IMPLEMENTED here!!!!
 Levels.Items items = new Levels.Items();
}

インターフェイスはインスタンス化されないため、インターフェイスをインスタンス化できないため、LEVELSインターフェイスをインスタンス化せずに、インターフェイス内のすべての要素にドット表記でアクセスできます。つまり、インターフェイス内で定義されたクラスを静的参照に対して透過的にすることができます。

そのため、Bが静的でない場合のItemsクラスと言っても意味がありません。ケースAとBの両方が同じ方法でインスタンス化されているため、静的または内部またはネストされているもののセマンティクスを探していません。セマンティクスに関する答えを私に与えるのをやめてください。コンパイラ、ランタイム、および動作の違い/利点が必要です。セマンティクスについてこれ以上答えないでください!!!!! JVMまたは.NETの専門家VM仕様は、教科書の意味論ではなく、この回答の質問を内包しています。

28
Blessed Geek

静的内部クラスは、内部クラスが外側のクラスのすべてのstatic変数とメソッドにアクセスできることを除いて、トップレベルのクラスとほとんど同じです。囲むクラス名は、内部クラスのパッケージ名前空間に効果的に追加されます。クラスを静的な内部クラスとして宣言することにより、クラスが囲んでいるクラスのコンテキストに何らかの方法で不可分に関連付けられていることを伝えます。

非静的内部クラスはあまり一般的ではありません。主な違いは、非静的内部クラスのインスタンスには、包含クラスのインスタンスへの暗黙的な参照が含まれ、その結果、その包含クラスインスタンスのインスタンス変数とメソッドにアクセスできることです。これにより、奇妙に見えるインスタンス化イディオムがいくつか発生します。次に例を示します。

Levels levels = new Levels(); // first need an instance of the enclosing class

// The items object contains an implicit reference to the levels object
Levels.Items items  = levels.new Items(); 

非静的内部クラスは、静的内部クラスよりも、それを囲むクラスとより密接に結びついています。それらには有効な用途があります(たとえば、反復子は、反復するデータ構造のクラス内の非静的内部クラスとして実装されることがよくあります)。

静的内部クラスの動作だけが本当に必要なときに、非静的内部クラスを宣言するのはよくある間違いです。

14
Henry

static内部クラスはネストされたクラスであり、非静的なクラスは内部クラスと呼ばれます。詳しくは ここを見る をご覧ください。

ただし、同じリンクからの抜粋を引用したい。

静的なネストされたクラスは、他のトップレベルクラスと同様に、その外部クラス(および他のクラス)のインスタンスメンバーと対話します。実際には、静的なネストされたクラスは、パッケージングの便宜上、別のトップレベルのクラスにネストされた動作としてトップレベルのクラスです。

2番目のケースでは、Word staticを使用しませんでした。そして、あなたはそれが暗黙的にstaticだと思います。それはそのインターフェースだからです。あなたはそれを仮定するのは正しいです。

内部クラスは、静的な入れ子クラスと同じように、実際にstatic入れ子クラスであるため、インターフェイスでインスタンス化できます。

Levels.Items hello = new Levels.Items();

したがって、上記のステートメントはどちらのケースでも有効です。最初のケースは静的なネストされたクラスで、2番目のケースではstaticを指定しませんでしたが、それでもインターフェース内にあるため、静的なネストされたクラスになります。 したがって、1つはクラスに入れ子にされ、もう1つはインターフェイスに入れ子にされているという事実以外に違いはありません。

通常、クラスの内部クラスはインターフェースにはありませんが、次のようにインスタンス化されます。

Levels levels = new Levels();
Levels.Items items = levels.new Items();

さらに、「非静的」内部クラスには、その外部クラスへの暗黙的な参照があります。これは、「静的」なネストされたクラスには当てはまりません。

27
Adeel Ansari

ネストされたクラスをインターフェイスで宣言する場合、それは常にpublicおよびstaticです。そう:

public interface Levels{
    class Items {
        public String value;
        public String path;

        public String getValue() {return value;}
    }
}

とまったく同じです

public interface Levels{
    public static class Items {
        public String value;
        public String path;

        public String getValue() {return value;}
    }
}

そして、

public interface Levels{
    static class Items {
        public String value;
        public String path;

        public String getValue() {return value;}
    }
}

私はこれをjavap -verboseでチェックしました、そしてそれらはすべて生成します

Compiled from "Levels.Java"
public class Levels$Items extends Java.lang.Object
  SourceFile: "Levels.Java"
  InnerClass: 
   public #14= #3 of #23; //Items=class Levels$Items of class Levels
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method   #4.#21; //  Java/lang/Object."<init>":()V
const #2 = Field    #3.#22; //  Levels$Items.value:Ljava/lang/String;
const #3 = class    #24;    //  Levels$Items
const #4 = class    #25;    //  Java/lang/Object
const #5 = Asciz    value;
const #6 = Asciz    Ljava/lang/String;;
const #7 = Asciz    path;
const #8 = Asciz    <init>;
const #9 = Asciz    ()V;
const #10 = Asciz   Code;
const #11 = Asciz   LineNumberTable;
const #12 = Asciz   LocalVariableTable;
const #13 = Asciz   this;
const #14 = Asciz   Items;
const #15 = Asciz   InnerClasses;
const #16 = Asciz   LLevels$Items;;
const #17 = Asciz   getValue;
const #18 = Asciz   ()Ljava/lang/String;;
const #19 = Asciz   SourceFile;
const #20 = Asciz   Levels.Java;
const #21 = NameAndType #8:#9;//  "<init>":()V
const #22 = NameAndType #5:#6;//  value:Ljava/lang/String;
const #23 = class   #26;    //  Levels
const #24 = Asciz   Levels$Items;
const #25 = Asciz   Java/lang/Object;
const #26 = Asciz   Levels;

{
public Java.lang.String value;

public Java.lang.String path;

public Levels$Items();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method Java/lang/Object."<init>":()V
   4:   return
  LineNumberTable: 
   line 2: 0

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      5      0    this       LLevels$Items;


public Java.lang.String getValue();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   getfield    #2; //Field value:Ljava/lang/String;
   4:   areturn
  LineNumberTable: 
   line 7: 0

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      5      0    this       LLevels$Items;


}

ネストされた/内部クラスの例は、(IMO)悪い例です。さらに、2番目の例は有効ではありませんJavaインターフェースは(暗黙的に)抽象メソッドしか宣言できないため、より良い例を次に示します:

public interface Worker {

    public class Response {
        private final Status status;
        private final String message;
        public Response(Status status, String message) {
            this.status = status; this.message = message;
        }
        public Status getStatus() { return status; }
        public String getMessage() { return message; }
    }

    ...

    public Response doSomeOperation(...);
}

Responseクラスを埋め込むことにより、それが他の用途のないWorker APIの基本的な部分であることを示しています。

Map.Entryクラスは、このイディオムのよく知られた例です。

7
Stephen C

IMHO、advantageは、プロジェクトフォルダーがごちゃごちゃしている場合、プロジェクトフォルダーを乱雑にするクラスが少なくなることです。 disadvantageは、内部クラスが要件の変更に伴って大きくなると、maintenacneが悪夢になるということです。

0
Ricky

最初のクラスは、Levelsクラスと、Itemsという静的内部クラスを宣言すると思いました。項目はLevels.Itemsによって参照される可能性があり、静的です。

2番目は、次のようにLevels.Itemsを使用してアクセスできる単純な内部クラスを宣言します。

Levels.Items hello = new Levels.Items();

編集:これは完全に間違っています、コメントやその他の返信を読んでください。

0
BlueTrin