Статический вложенный класс в Java, почему? - PullRequest
201 голосов
/ 31 октября 2008

Я просматривал код Java для LinkedList и заметил, что он использует статический вложенный класс, Entry.

public class LinkedList<E> ... {
...

 private static class Entry<E> { ... }

}

В чем причина использования статического вложенного класса, а не обычного внутреннего класса?

Единственная причина, о которой я мог подумать, это то, что Entry не имеет доступа к переменным экземпляра, поэтому с точки зрения ООП он лучше инкапсулируется.

Но я подумал, что могут быть и другие причины, например, производительность. Что бы это могло быть?

Примечание. Я надеюсь, что мои термины верны, я бы назвал это статическим внутренним классом, но я думаю, что это неправильно: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

Ответы [ 13 ]

257 голосов
/ 31 октября 2008

На странице Sun, на которую вы ссылаетесь, есть несколько ключевых отличий:

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

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

Нет необходимости, чтобы LinkedList.Entry был классом верхнего уровня, поскольку только используется LinkedList (есть некоторые другие интерфейсы, которые также имеют статические вложенные классы с именем Entry, например как Map.Entry - та же концепция). А поскольку ему не нужен доступ к членам LinkedList, имеет смысл сделать его статичным - это гораздо более чистый подход.

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

43 голосов
/ 31 октября 2008

На мой взгляд, вопрос должен быть наоборот, когда вы видите внутренний класс - действительно ли он должен быть внутренним классом с дополнительной сложностью и неявным (а не явным и более понятно, IMO) ссылка на экземпляр содержащего класса?

Имейте в виду, я предвзят как фанат C # - C # не имеет эквивалента внутренних классов, хотя у него есть вложенные типы. Я не могу сказать, что я пропустил внутренние занятия:)

26 голосов
/ 31 октября 2008

Здесь есть неочевидные проблемы с хранением памяти, которые необходимо учитывать. Так как нестатический внутренний класс поддерживает неявную ссылку на свой «внешний» класс, если на экземпляр внутреннего класса жестко ссылаются, то на внешний экземпляр также жестко ссылаются. Это может привести к некоторым царапинам, когда внешний класс не собирается сборщиком мусора, даже если кажется , что ничто не ссылается на него.

10 голосов
/ 31 октября 2008

Ну, во-первых, нестатические внутренние классы имеют дополнительное скрытое поле, которое указывает на экземпляр внешнего класса. Поэтому, если бы класс Entry не был статическим, то, кроме того, что у него не было доступа, он имел бы четыре указателя вместо трех.

Как правило, я бы сказал, что если вы определяете класс, который в основном должен действовать как набор элементов данных, например, как "struct" в C, рассмотрите возможность сделать его статическим.

7 голосов
/ 26 октября 2016

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

class OuterClass {
    private OuterClass(int x) {
        System.out.println("x: " + x);
    }

    static class InnerClass {
        public static void test() {
            OuterClass outer = new OuterClass(1);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        OuterClass.InnerClass.test();
        // OuterClass outer = new OuterClass(1); // It is not possible to create outer instance from outside.
    }
}

Будет выведено x: 1

7 голосов
/ 16 декабря 2013

Статический вложенный класс такой же, как и любой другой внешний класс, так как он не имеет доступа к членам внешнего класса.

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

Пример такого использования вы можете найти в файле Android R.java (resources). Папка Res Android содержит макеты (содержащие рисунки экрана), папку для рисования (содержащую изображения, используемые для проекта), папку значений (которая содержит строковые константы) и т. Д.

Так как все папки являются частью папки Res, android tool генерирует файл R.java (resources), который внутри содержит множество статических вложенных классов для каждой из их внутренних папок.

Вот как выглядит файл R.java, созданный в Android: Здесь они используются только для удобства упаковки.

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.techpalle.b17_testthird;

public final class R {
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int main=0x7f070000;
    }
    public static final class string {
        public static final int action_settings=0x7f050001;
        public static final int app_name=0x7f050000;
        public static final int hello_world=0x7f050002;
    }
}
6 голосов
/ 21 ноября 2013

С http://docs.oracle.com/javase/tutorial/java/javaOO/whentouse.html:

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

4 голосов
/ 31 октября 2008

Простой пример:

package test;

public class UpperClass {
public static class StaticInnerClass {}

public class InnerClass {}

public static void main(String[] args) {
    // works
    StaticInnerClass stat = new StaticInnerClass();
    // doesn't compile
    InnerClass inner = new InnerClass();
}
}

Если нестатический класс, экземпляр не может быть создан, за исключением экземпляра высшего класса (не в примере, где main является статической функцией)

2 голосов
/ 31 октября 2008

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

PS: я всегда понимал, что «вложенные» и «внутренние» взаимозаменяемы. В этих терминах могут быть тонкие нюансы, но большинство разработчиков Java поймут это.

1 голос
/ 24 ноября 2011

Нестатические внутренние классы могут привести к утечкам памяти, в то время как статический внутренний класс защитит от них. Если внешний класс содержит значительные данные, это может снизить производительность приложения.

...