Внутренний класс в интерфейсе против в классе - PullRequest
28 голосов
/ 04 декабря 2009

В чем разница между этими двумя объявлениями внутреннего класса? Также прокомментируйте преимущества / недостатки?

case 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.

дополнительная информация: Я могу создать экземпляр класса Items в обоих случаях A и B в другом классе, который не реализует интерфейс ВСЕ.

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

Так как интерфейс не создается, все элементы внутри интерфейса доступны посредством точечной нотации без создания интерфейса LEVELS просто потому, что вы не можете создать экземпляр интерфейса - эффективно делая класс, определенный внутри интерфейса, проницаемым для статической ссылки.

То, что класс Items в случае B не является статическим, не имеет смысла. Поскольку оба случая A и B создаются одинаково, я не ищу семантику статического, внутреннего или вложенного. Прекратите давать мне ответы на семантику. Я хочу, чтобы компилятор, различия во времени и поведенческие различия / преимущества, или, если нет, то так и скажите. Нет больше ответов по семантике, пожалуйста !!!!! Эксперт по спецификациям JVM или .NET VM, пожалуйста, ответьте на этот вопрос, а не на семантикусы учебников.

Ответы [ 6 ]

27 голосов
/ 04 декабря 2009

Внутренний класс static является вложенным классом, а нестатический называется внутренним классом. Для более подробной информации смотрите здесь .

Однако мне нравится цитировать выдержку из той же ссылки.

Статический вложенный класс взаимодействует с члены экземпляра его внешнего класс (и другие классы) так же, как любой другой класс высшего уровня. В результате, статический вложенный класс поведенчески класс верхнего уровня, который был вложенным в другом классе верхнего уровня для удобство упаковки.

Вы не использовали слово static во втором случае. И вы думаете, что это будет неявно static, потому что это интерфейс. Вы правы, предполагая это.

Вы можете создать экземпляр внутреннего класса в своем интерфейсе, как статический вложенный класс, потому что это действительно static вложенный класс.

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

Итак, приведенное выше утверждение будет действительным в обоих ваших случаях . В первом случае используется статический вложенный класс, а во втором случае вы не указали static, но даже в этом случае это будет статический вложенный класс, поскольку он находится в интерфейсе. Следовательно, нет никакой разницы, кроме того факта, что один вложен в класс, а другой - в интерфейс .

Обычно внутренний класс в классе, не в интерфейсе , создается, как показано ниже.

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

Более того, «нестатический» внутренний класс будет иметь неявную ссылку на свой внешний класс. Это не относится к «статическому» вложенному классу.

15 голосов
/ 02 апреля 2012

Если вы объявляете вложенный класс в интерфейсе, это всегда 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;


}
14 голосов
/ 04 декабря 2009

Статические внутренние классы в основном похожи на классы верхнего уровня, за исключением того, что внутренний класс имеет доступ ко всем статическим переменным и методам включающего класса. Имя окружающего класса эффективно добавляется к пространству имен пакета внутреннего класса. Объявляя класс статическим внутренним классом, вы сообщаете, что класс каким-то образом неразрывно связан с контекстом включающего класса.

Нестатические внутренние классы встречаются реже. Основное отличие состоит в том, что экземпляры нестатического внутреннего класса содержат неявную ссылку на экземпляр включающего класса, и в результате имеют доступ к переменным экземпляра и методам этого экземпляра включающего класса. Это приводит к некоторым странным идиомам инстанцирования, например:

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(); 

Нестатические внутренние классы гораздо более тесно связаны со своими классами, чем статические внутренние классы. Они имеют правильное использование (например, итераторы часто реализуются как нестатические внутренние классы внутри класса структуры данных, по которой они повторяются).

Распространенной ошибкой объявлять нестатический внутренний класс, когда вам действительно нужно только статическое поведение внутреннего класса.

7 голосов
/ 04 декабря 2009

Примеры, которые вы приводите для вложенных / внутренних классов, являются (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 является хорошо известным примером этой идиомы.

0 голосов
/ 04 декабря 2009

Я думал, что первый объявит класс Levels и статический внутренний класс Items. Элементы могут ссылаться на Levels.Items и будут статическими.

В то время как второй объявит простой внутренний класс, доступ к которому можно получить с помощью Levels.Items, как показано ниже:

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

РЕДАКТИРОВАТЬ: это совершенно неправильно, прочитайте комментарии и другие ответы.

0 голосов
/ 04 декабря 2009

ИМХО, преимущество в том, что у вас меньше классов, загромождающих папку вашего проекта, если они тривиальны; недостаток заключается в том, что когда ваш внутренний класс растет вместе с изменением требований, maintenacne становится вашим кошмаром.

...