Вопрос

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

случай А:класс внутри класса.

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

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

и случай Б:класс внутри интерфейса.

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 лучше ответить на этот вопрос, а не на семантику из учебника.

Это было полезно?

Решение

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

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

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

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

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

Другие советы

Ан static внутренний класс является вложенным классом, а нестатический класс называется внутренним классом.Для большего, Смотри сюда.

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

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

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

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

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

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

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

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

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

Если вы объявляете вложенный класс в интерфейсе, он всегда общественный и статический.Так:

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) плохими примерами.Кроме того, второй пример недопустим для 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 — хорошо известный пример этой идиомы.

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top