Statycznie zagnieżdżona klasa w Javie, dlaczego?

217

Szukałem na kod Java dla LinkedListi zauważył, że wszystko jest używać statycznego zagnieżdżonych klasy Entry.

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

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

}

Jaki jest powód używania statycznej klasy zagnieżdżonej zamiast normalnej klasy wewnętrznej?

Jedynym powodem, o którym mogłem pomyśleć, było to, że Entry nie ma dostępu do zmiennych instancji, więc z punktu widzenia OOP ma lepszą enkapsulację.

Ale pomyślałem, że mogą być inne powody, może wydajność. Co to może być

Uwaga. Mam nadzieję, że moje warunki są prawidłowe, nazwałbym to statyczną klasą wewnętrzną, ale myślę, że to źle: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

David Turner
źródło

Odpowiedzi:

271

Strona Sun, do której prowadzi link, ma kilka kluczowych różnic między nimi:

Zagnieżdżona klasa jest członkiem jej klasy zamykającej. Niestatyczne klasy zagnieżdżone (klasy wewnętrzne) mają dostęp do innych członków klasy zamykającej, nawet jeśli są zadeklarowane jako prywatne. Statycznie zagnieżdżone klasy nie mają dostępu do innych członków klasy zamykającej.
...

Uwaga: Statycznie zagnieżdżona klasa współdziała z elementami instancji swojej klasy zewnętrznej (i innych klas), tak jak każda inna klasa najwyższego poziomu. W efekcie statyczna klasa zagnieżdżona jest behawioralnie klasą najwyższego poziomu, która została zagnieżdżona w innej klasie najwyższego poziomu dla wygody pakowania.

Nie ma potrzeby, LinkedList.Entryaby istniała klasa najwyższego poziomu, ponieważ jest ona używana tylko przez LinkedList(istnieją inne interfejsy, które mają również nazwane statyczne klasy zagnieżdżone Entry, takie jak Map.Entry- ta sama koncepcja). A ponieważ nie potrzebuje on dostępu do członków LinkedList, ma sens, aby był statyczny - jest to znacznie czystsze podejście.

Jak zauważa Jon Skeet , myślę, że lepszym pomysłem jest, jeśli używasz zagnieżdżonej klasy, aby zacząć od statyczności, a następnie zdecydować, czy naprawdę musi ona być niestatyczna na podstawie twojego użycia.

matowy b
źródło
Nie wydaje mi się, żebym mógł uzyskać link do komentarza do pracy, ale jest to ten komentarz:#comment113712_253507
Zach Lysobey,
1
@matt b Jeśli statyczna klasa zagnieżdżona nie ma dostępu do elementów instancji klasy Outer, w jaki sposób współdziała z elementami instancji klasy Outer?
Geek
1
@mattb Ale jak zauważył @Geek, strona Słońca jest sprzeczna: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class jak to możliwe, jeśli tylko akapit przed dokumentami mówi: Static nested classes do not have access to other members of the enclosing class Może chcieliby powiedzieć: 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 Dzięki za link! Tak, myliłem się, czytając mój komentarz, teraz widzę, że moje wyrażenie było niepoprawne. Jak powiedział An inner class interacts with the instance members through an implicit reference to its enclosing class, a ten zwraca uwagę, innym interesującym mienia non-static inner classesjak również anonymous inner classesczy local classes defined inside a block: wszystkie one nie mogą mieć do no-argkonstruktora spowodować kompilator niejawnie poprzedzić sekwencję arg każdego konstruktora, aby przekazać referencję instancji załączania klasa. Dość proste.
tonix,
1
możesz użyć statycznej klasy wewnętrznej, aby utworzyć instancję klasy zewnętrznej, która ma tylko prywatnego konstruktora. Jest to używane we wzorcu konstruktora. Nie możesz zrobić tego samego z klasą wewnętrzną.
seenimurugan
47

Moim zdaniem pytanie powinno być odwrotne, ilekroć zobaczysz klasę wewnętrzną - czy naprawdę musi to być klasa wewnętrzna, z dodatkową złożonością i niejawnym (a nie wyraźnym i wyraźniejszym odniesieniem do IMO) do instancji klasy zawierającej?

Pamiętaj, że jestem stronniczy jako fan C # - C # nie ma odpowiednika klas wewnętrznych, chociaż ma zagnieżdżone typy. Nie mogę powiedzieć, że opuściłem jeszcze klasy wewnętrzne :)

Jon Skeet
źródło
4
Mogę się mylić, ale wydaje mi się, że jest to przykład statycznej klasy zagnieżdżonej, a nie klasy wewnętrznej. W przykładzie podają nawet, że nie mają dostępu do zmiennych instancji w otaczającej klasie w klasie zagnieżdżonej.
ColinD
Tak, Colin ma rację - C # nie ma wewnętrznych klas, ma zagnieżdżone klasy. Pamiętaj, że statycznie zagnieżdżona klasa w C # nie jest tym samym, co statyczna zagnieżdżona klasa w Javie :)
Jon Skeet
2
Typy zagnieżdżone to jeden z tych obszarów, w których język C # jest wyjątkowo poprawny w porównaniu z Javą. Zawsze
dziwię się
3
@nawfal: Tak, pomijając kilka drobiazgów, jestem pod wrażeniem tego, jak dobrze język C # został zaprojektowany (i określony).
Jon Skeet,
1
@JonSkeet, czy masz artykuł lub blog na temat tych czarnuchów? Chciałbym przejść przez to, co znajdziesz jako „niggles” :)
nawfal
27

Należy wziąć pod uwagę nieoczywiste problemy z pamięcią. Ponieważ niestatyczna klasa wewnętrzna utrzymuje niejawne odniesienie do swojej klasy „zewnętrznej”, jeśli instancja klasy wewnętrznej jest silnie przywołana, to również instancja zewnętrzna jest silnie przywołana. Może to prowadzić do porysowania głowy, gdy klasa zewnętrzna nie jest gromadzona, nawet jeśli wydaje się, że nic do niej nie odwołuje się.

Leigh
źródło
Jeśli „zewnętrzna” klasa jest ostateczna i dlatego nie można jej w ogóle utworzyć instancji, to czy ten argument ma sens w takim przypadku? Ponieważ posiadanie / utrzymywanie odwołania do klasy zewnętrznej jest bezużyteczne, jeśli ta ostatnia jest ostateczna.
getsadzeg
10

Po pierwsze, niestatyczne statyczne klasy wewnętrzne mają dodatkowe, ukryte pole, które wskazuje na instancję klasy zewnętrznej. Więc jeśli klasa Entry nie byłaby statyczna, to oprócz tego, że nie potrzebuje dostępu, nosiłaby cztery wskaźniki zamiast trzech.

Zasadniczo powiedziałbym, że jeśli zdefiniujesz klasę, która zasadniczo działa jako zbiór elementów danych, takich jak „struct” w C, rozważ ustawienie statyczne.


źródło
8

Statyczna klasa wewnętrzna jest używana we wzorcu konstruktora. Statyczna klasa wewnętrzna może tworzyć instancję swojej klasy zewnętrznej, która ma tylko prywatnego konstruktora. Możesz więc użyć statycznej klasy wewnętrznej do utworzenia klasy zewnętrznej, która ma tylko prywatnego konstruktora. Nie możesz zrobić tego samego z klasą wewnętrzną, ponieważ musisz mieć obiekt klasy zewnętrznej utworzony przed uzyskaniem dostępu do klasy wewnętrznej.

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

To wyświetli x: 1

seenimurugan
źródło
7

statyczna klasa zagnieżdżona jest jak każda inna klasa zewnętrzna, ponieważ nie ma dostępu do elementów klasy zewnętrznej.

Tylko dla wygody pakowania możemy pogrupować statycznie zagnieżdżone klasy w jedną klasę zewnętrzną w celu zapewnienia czytelności. Poza tym nie ma innego przypadku użycia statycznej klasy zagnieżdżonej.

Przykład takiego zastosowania można znaleźć w pliku Android R.java (zasoby). Folder Res Androida zawiera układy (zawierające projekty ekranów), folder do rysowania (zawierający obrazy użyte w projekcie), folder wartości (który zawiera stałe ciągów) itp.

Ponieważ wszystkie foldery są częścią folderu Res, narzędzie Android generuje plik R.java (zasoby), który wewnętrznie zawiera wiele statycznie zagnieżdżonych klas dla każdego z ich wewnętrznych folderów.

Oto wygląd pliku R.java wygenerowanego w Androidzie: tutaj używają tylko dla wygody pakowania.

/* 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;
    }
}
użytkownik1923551
źródło
4

Prosty przykład:

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

Jeśli nie jest statyczny, nie można utworzyć instancji klasy z wyjątkiem instancji wyższej klasy (więc nie w przykładzie, w którym main jest funkcją statyczną)

Vinze
źródło
twoja StaticInnerClass nie jest w rzeczywistości statyczną klasą zagnieżdżoną / wewnętrzną. jest to klasa statyczna najwyższego poziomu.
theRiley,
2

Jednym z powodów statycznego vs. normalnego jest ładowanie klas. Nie można utworzyć instancji klasy wewnętrznej w konstruktorze jej rodzica.

PS: Zawsze rozumiałem „zagnieżdżone” i „wewnętrzne” jako wymienne. Mogą występować subtelne niuanse, ale większość programistów Java też to zrozumie.

Mark Renouf
źródło
1

Niestatyczne klasy wewnętrzne mogą powodować wycieki pamięci, podczas gdy statyczne klasy wewnętrzne będą przed nimi chronić. Jeśli klasa zewnętrzna przechowuje znaczne dane, może obniżyć wydajność aplikacji.

Broń
źródło
„static static” jest wewnętrznie sprzecznością.
Markiz Lorne
1
@EJP, sheesh ... ludzie naprawdę-off, wskazując na to uwagę każdej chwili ktoś wspomina o „statyczne klasy wewnętrzne” ...
Sakiboy
0

Nie wiem o różnicy w wydajności, ale jak mówisz, statyczna klasa zagnieżdżona nie jest częścią instancji klasy zamykającej. Wydaje się po prostu łatwiej stworzyć statyczną klasę zagnieżdżoną, chyba że naprawdę potrzebujesz, aby była to klasa wewnętrzna.

To trochę tak, dlaczego zawsze zmieniam moje zmienne na Javę - jeśli nie są ostateczne, wiem, że dzieje się z nimi coś śmiesznego. Jeśli używasz klasy wewnętrznej zamiast statycznej klasy zagnieżdżonej, powinien istnieć dobry powód.


źródło
Klasa wewnętrzna również nie jest „częścią instancji klasy zamykającej”.
Markiz Lorne
klasa wewnętrzna jest egzystencjalnie zależna od klasy zamykającej i ma intymny dostęp do członków klasy zamykającej, więc w rzeczywistości jest częścią klasy zamykającej. w rzeczywistości jest członkiem.
theRiley,
0

Użycie statycznej klasy zagnieżdżonej zamiast niestatycznej może w niektórych przypadkach zaoszczędzić miejsce. Na przykład: zaimplementowanie Comparatorklasy wewnątrz, powiedz Student.

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() {...}
  }
}

Następnie staticzapewnia, że ​​klasa ucznia ma tylko jeden komparator, zamiast tworzyć nową za każdym razem, gdy tworzona jest nowa instancja ucznia.

JenkinsY
źródło
-1

Zaleta klasy wewnętrznej

  1. jednorazowe użycie
  2. obsługuje i poprawia enkapsulację
  3. czytelność
  4. prywatny dostęp do pola

Bez istnienia klasy zewnętrznej klasa wewnętrzna nie istniałaby.

class car{
    class wheel{

    }
}

Istnieją cztery rodzaje klas wewnętrznych.

  1. normalna klasa wewnętrzna
  2. Metoda lokalna klasa wewnętrzna
  3. Anonimowa klasa wewnętrzna
  4. statyczna klasa wewnętrzna

punkt ---

  1. ze statycznej klasy wewnętrznej możemy uzyskać dostęp tylko do statycznego elementu klasy zewnętrznej.
  2. Wewnątrz klasy wewnętrznej nie możemy zadeklarować członka statycznego.
  3. w celu wywołania normalnej klasy wewnętrznej w statycznym obszarze klasy zewnętrznej.

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

  4. w celu wywołania normalnej klasy wewnętrznej w instancji obszaru klasy zewnętrznej.

    Inner i=new Inner();

  5. w celu wywołania normalnej klasy wewnętrznej poza klasą zewnętrzną.

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

  6. inside Klasa wewnętrzna Ten wskaźnik do klasy wewnętrznej.

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

  7. dla klasy wewnętrznej obowiązującym modyfikatorem jest - public, default,

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

  8. outer $ inner to nazwa klasy wewnętrznej.

  9. metodę klasy wewnętrznej wewnątrz instancji, wtedy możemy uzyskać dostęp do pola statycznego i instancji klasy zewnętrznej.

10. wewnętrzna klasa wewnątrz metody statycznej, wtedy możemy uzyskać dostęp tylko do pola statycznego

klasa zewnętrzna.

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
            }
        }
    }
}
Gaurav Tiwari
źródło