Статичний вкладений клас на Java, чому?


217

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

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

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

}

Яка причина використання статичного вкладеного класу, а не звичайного внутрішнього класу?

Єдиною причиною, про яку я міг подумати, було те, що Entry не має доступу до змінних екземплярів, тому з точки зору OOP вона має кращу інкапсуляцію.

Але я думав, що можуть бути й інші причини, можливо, продуктивність. Що це може бути?

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


Відповіді:


271

Сторінка Sun, на яку ви посилаєтесь, має деякі ключові відмінності між цими двома:

Вкладений клас є членом його класу, що додається. Нестатичні вкладені класи (внутрішні класи) мають доступ до інших членів класу, що додається, навіть якщо вони оголошені приватними. Статичні вкладені класи не мають доступу до інших членів класу, що додається.
...

Примітка: Статичний вкладений клас взаємодіє з членами екземпляра його зовнішнього класу (та інших класів), як і будь-який інший клас вищого рівня. Фактично, статичний вкладений клас є поведінковим класом вищого рівня, який був вкладений в інший клас верхнього рівня для зручності упаковки.

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

Як зазначає Джон Скіт , я думаю, що краще, якщо ви використовуєте вкладений клас - це почати з того, що він є статичним, а потім вирішити, чи дійсно він повинен бути нестатичним на основі вашого використання.


Ба, я не можу отримати прив'язку до коментаря для роботи, але його цей коментар:#comment113712_253507
Зах Лисобей

1
@matt b Якщо статичний вкладений клас не має доступу до членів екземпляра Зовнішнього класу, як він взаємодіє з членами екземпляра Зовнішнього класу?
Geek

1
@mattb Але як зауважив @Geek, сторінка Sun суперечлива: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class Як це можливо, якщо лише абзац перед документами сказав: Static nested classes do not have access to other members of the enclosing class Можливо, вони хотіли б сказати: A nested (non-static) class interacts with the instance members of its outer class (and other classes) just like any other top-level class
tonix

1
@DavidS Дякую за посилання! Так, я помилявся, читаючи коментар зараз, я бачу, що моя перефразовування була неправильною. Як ви сказали: An inner class interacts with the instance members through an implicit reference to its enclosing classі це свідчить ще одна цікава властивість non-static inner classes, а також anonymous inner classesчи local classes defined inside a block: вони не можуть мати в no-argконструктор причина в компілятор неявно випереджати послідовність Arg кожного конструктора, щоб передати посилання на екземпляр осяжний клас. Досить просто.
тонікс

1
ви можете використовувати статичний внутрішній клас для інстанціювання зовнішнього класу, який має лише приватний конструктор. Це використовується в шаблоні будівельника. Не можна робити те ж саме з внутрішнім класом.
sawimurugan

47

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

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


4
Я можу помилитися, але це виглядає як приклад статичного вкладеного класу, а не внутрішнього класу. Вони навіть в прикладі вказують, що вони не мають доступу до змінних примірників навколишнього класу вкладеного класу.
ColinD

Так, справа Коліна - у C # немає внутрішніх класів, у нього вкладені класи. Майте на увазі, статичний вкладений клас у C # - це не те саме, що статичний вкладений клас у Java :)
Джон Скіт

2
Вкладені типи - одна з тих областей, де C # отримав це вкрай коректно порівняно з Java. Я завжди
дивуюсь

3
@nawfal: Так, забороняючи кілька нігтів, я в захваті від того, наскільки добре розроблена (і вказана) мова C #.
Джон Скіт

1
@JonSkeet Чи є у вас стаття чи блог про те, що ці нігері? Я хотів би перейти через те, що ти
вважаєш

27

Тут слід враховувати неочевидні проблеми із збереженням пам’яті. Оскільки нестатичний внутрішній клас підтримує неявну посилання на його "зовнішній" клас, якщо на екземпляр внутрішнього класу сильно посилається, то на зовнішній екземпляр також сильно посилається. Це може призвести до певного вискоблювання голови, коли зовнішній клас не збирається сміттям, хоча здається, що нічого не посилається на це.


Якщо "зовнішній" клас є остаточним і тому його взагалі неможливо встановити, чи має сенс цей аргумент у такому випадку? Оскільки мати / зберігати посилання на зовнішній клас марно, якщо останній є остаточним.
getadzeg

10

Ну, для одного, нестатичні внутрішні класи мають додаткове приховане поле, яке вказує на екземпляр зовнішнього класу. Отже, якщо клас "Введення" не був би статичним, то, окрім доступу, який йому не потрібен, він мав би навколо чотирьох покажчиків замість трьох.

Як правило, я б сказав, якщо ви визначите клас, який в основному існує, щоб він діяв як набір членів даних, як-от "структура" в C, подумайте про його статичність.


8

Статичний внутрішній клас використовується в побудові конструкції. Статичний внутрішній клас може створювати зовнішній клас, який має лише приватний конструктор. Таким чином, ви можете використовувати статичний внутрішній клас для інстанціювання зовнішнього класу, який має лише приватний конструктор. Ви не можете зробити те ж саме з внутрішнім класом, як вам потрібно, щоб об'єкт зовнішнього класу був створений до доступу до внутрішнього класу.

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

статичний вкладений клас подібно до будь-якого іншого зовнішнього класу, оскільки не має доступу до зовнішніх членів класу.

Тільки для зручності упаковки ми можемо об'єднати статичні вкладені класи в один зовнішній клас з метою читабельності. Крім цього немає іншого випадку використання статичного вкладеного класу.

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

Sine всі папки є частиною папки Res, а інструмент Android створює файл R.java (ресурси), який внутрішньо містить безліч статичних вкладених класів для кожної їх внутрішньої папки.

Ось вигляд файлу 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

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

Використовуйте нестатичний вкладений клас (або внутрішній клас), якщо вам потрібен доступ до непублічних полів і методів огороджувального примірника. Використовуйте статичний вкладений клас, якщо вам не потрібен цей доступ.


4

Простий приклад:

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

Якщо нестатичний, клас не може бути екземпляр екзептований у екземплярі верхнього класу (так не у прикладі, де головним є статична функція)


Ваш StaticInnerClass насправді не є статичним вкладеним / внутрішнім класом. це статичний клас вищого рівня.
theRiley

2

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

PS: Я завжди розумів, що "вкладені" та "внутрішні" є взаємозамінними. У термінах можуть бути тонкі нюанси, але більшість розробників Java розуміє будь-яке.


1

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


"статичний внутрішній" - це суперечність в термінах.
Маркіз Лорн

1
@EJP, sheesh ... люди дійсно відходять, вказуючи це будь-коли, коли хтось згадає про "статичні внутрішні класи" ...
Sakiboy

0

Я не знаю про різницю в продуктивності, але, як ви кажете, статичний вкладений клас не є частиною екземпляра класу, що вкладається. Здається, просто створити статичний вкладений клас, якщо вам справді не потрібен внутрішній клас.

Це трохи схоже на те, чому я завжди роблю свої змінні остаточними в Java - якщо вони не остаточні, я знаю, що з ними відбувається щось смішне. Якщо ви використовуєте внутрішній клас замість статичного вкладеного класу, це має бути вагома причина.


Клас інтернеру також не є "частиною екземпляра класу, що вкладається".
Маркіз Лорн

внутрішній клас істотно залежить від класу, що додає, і має інтимний доступ до членів класу, що додає, тому він фактично є частиною класу, що охоплює. насправді це член.
theRiley

0

Використання статичного вкладеного класу, а не нестатичного, в деяких випадках може економити пробіли. Наприклад: реалізація Comparatorвсередині класу, скажімо, студент.

public class Student {
  public static final Comparator<Student> BY_NAME = new ByName();
  private final String name;
  ...
  private static class ByName implements Comparator<Student> {
    public int compare() {...}
  }
}

Тоді staticгарантується, що в класі Student є лише один Порівняльник, а не примірник нового при кожному створенні нового екземпляра студента.


-1

Перевага внутрішнього класу--

  1. одноразове використання
  2. підтримує та покращує інкапсуляцію
  3. читальність
  4. приватний доступ до поля

Без існуючого зовнішнього класу внутрішній клас не існуватиме.

class car{
    class wheel{

    }
}

Існує чотири типи внутрішнього класу.

  1. нормальний внутрішній клас
  2. Метод Місцевий внутрішній клас
  3. Анонімний внутрішній клас
  4. статичний внутрішній клас

точка ---

  1. З статичного внутрішнього класу ми можемо отримати доступ лише до статичного члена зовнішнього класу.
  2. Всередині внутрішнього класу ми не можемо оголосити статичний член.
  3. не вимагати виклику нормального внутрішнього класу в статичній області зовнішнього класу.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  4. inorder для виклику нормального внутрішнього класу, наприклад, область зовнішнього класу.

    Inner i=new Inner();

  5. не вимагати виклику нормального внутрішнього класу поза зовнішнього класу.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  6. всередині Внутрішній клас Цей вказівник на внутрішній клас.

    this.member-current inner class outerclassname.this--outer class

  7. для внутрішнього класу застосований модифікатор - public, default,

    final,abstract,strictfp,+private,protected,static

  8. external $ inner - це ім'я внутрішнього класу.

  9. метод внутрішнього класу всередині методу екземпляра, тоді ми можемо отримати статичне та поле екземпляра зовнішнього класу.

10.inner клас всередині статичного методу, тоді ми можемо отримати доступ лише до статичного поля

зовнішній клас.

class outer{

    int x=10;
    static int y-20;

    public void m1() {
        int i=30;
        final j=40;

        class inner{

            public void m2() {
                // have accees x,y and j
            }
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.