Dlaczego zewnętrzne klasy Java mogą uzyskiwać dostęp do prywatnych członków klasy wewnętrznej?

177

Zauważyłem, że klasy zewnętrzne mogą uzyskać dostęp do prywatnych zmiennych instancji klas wewnętrznych. Jak to jest możliwe? Oto przykładowy kod demonstrujący to samo:

class ABC{
    class XYZ{
        private int x=10;
    }

    public static void main(String... args){
        ABC.XYZ xx = new ABC().new XYZ();
        System.out.println("Hello :: "+xx.x); ///Why is this allowed??
    }
}

Dlaczego takie zachowanie jest dozwolone?

Harish
źródło
To pytanie mylić mnie przez jakiś czas, dopóki nie zobaczę komentarz ... to wyjaśnia, dlaczego nie mogę dostęp XX.X na moim komputerze ..
Wang Sheng
4
Komentarze mnie zmyliły, powyższy kod uruchamiam w Javie 8, kompiluje się i uruchamia. xx.x jest dostępny.
Leon
Harish, czy możesz cofnąć zaakceptowaną odpowiedź (która nie odpowiada na zadane przez Ciebie pytanie) i zamiast tego zaakceptować odpowiedź Martina Anderssona poniżej, która bardzo dokładnie odpowiada na nią?
temporary_user_name
FWIW ta składnia jest absolutnie przerażająca:new ABC().new XYZ()
Josh M.

Odpowiedzi:

80

Klasa wewnętrzna jest po prostu sposobem na czyste oddzielenie niektórych funkcji, które naprawdę należą do oryginalnej klasy zewnętrznej. Są przeznaczone do użycia, gdy masz 2 wymagania:

  1. Niektóre funkcje w twojej zewnętrznej klasie byłyby najbardziej oczywiste, gdyby zostały zaimplementowane w osobnej klasie.
  2. Mimo że jest to osobna klasa, funkcjonalność jest bardzo ściśle związana ze sposobem działania klasy zewnętrznej.

Biorąc pod uwagę te wymagania, klasy wewnętrzne mają pełny dostęp do swojej klasy zewnętrznej. Ponieważ są w zasadzie członkami klasy zewnętrznej, ma sens, że mają dostęp do metod i atrybutów klasy zewnętrznej - w tym szeregowych.

Kaleb Brasee
źródło
217
Ta odpowiedź wyjaśnia, dlaczego klasy zagnieżdżone mają dostęp do prywatnych członków swojej klasy zewnętrznej. Powstaje jednak pytanie, dlaczego klasa zewnętrzna ma dostęp do prywatnych członków klas zagnieżdżonych.
Andrew,
13
Po prostu dodaj „i vice versa”. Biorąc pod uwagę te wymagania, klasy wewnętrzne mają pełny dostęp do swojej klasy zewnętrznej i teraz ona odpowiada na pytanie.
anthropomo
13
To nie jest poprawną odpowiedzią na ten problem, tutaj: stackoverflow.com/questions/19747812/…
Colin Su
4
@anthropomo: Nie, tak nie jest. Oba wymagania są całkowicie wykonalne bez dostępu klasy zewnętrznej do prywatnych członków klas wewnętrznych.
LUB Mapper
Dobrym przykładem, w którym jest to szczególnie przydatne, jest wzorzec konstruktora, stackoverflow.com/a/1953567/1262000 . Klasa nadrzędna potrzebuje tylko konstruktora, który pobiera Konstruktora i uzyskuje dostęp do wszystkich jego zmiennych składowych. W przeciwnym razie musiałbyś mieć prywatnego konstruktora w klasie nadrzędnej ze wszystkimi prywatnymi zmiennymi składowymi.
vikky.rk
62

Jeśli chcesz ukryć prywatnych członków swojej klasy wewnętrznej, możesz zdefiniować interfejs z członkami publicznymi i utworzyć anonimową klasę wewnętrzną, która implementuje ten interfejs. Przykład poniżej:

class ABC{
    private interface MyInterface{
         void printInt();
    }

    private static MyInterface mMember = new MyInterface(){
        private int x=10;

        public void printInt(){
            System.out.println(String.valueOf(x));
        }
    };

    public static void main(String... args){
        System.out.println("Hello :: "+mMember.x); ///not allowed
        mMember.printInt(); // allowed
    }
}
Ich
źródło
To jest genialny fragment kodu. Dokładnie to, czego potrzebowałem. Dzięki!
kevinarpe
Podaj kod, który może być uruchomiony. Co więcej, proszę podać powód, dla którego zmienna prywatna nie mogła uzyskać dostępu.
androidyue
7
Ale potem ... klasa wewnętrzna jest anonimowa. Nie możesz utworzyć kilku wystąpień tej klasy wewnętrznej ani użyć tej klasy wewnętrznej do deklaracji zmiennych itp.
LUB Mapper
@OR Mapper, dlatego nawet jeśli xjest tutaj publiczny, nie jest dozwolony jak mMember.x.
MAC,
54

Klasa wewnętrzna jest (do celów kontroli dostępu) uważana za część klasy zawierającej. Oznacza to pełny dostęp do wszystkich szeregowych.

Sposób implementacji polega na użyciu syntetycznych metod chronionych przez pakiet: klasa wewnętrzna zostanie skompilowana do oddzielnej klasy w tym samym pakiecie (ABC $ XYZ). JVM nie obsługuje bezpośrednio tego poziomu izolacji, więc na poziomie kodu bajtowego ABC $ XYZ będzie mieć metody chronione pakietem, których klasa zewnętrzna używa do uzyskiwania dostępu do prywatnych metod / pól.

Thilo
źródło
17

Pojawia się poprawna odpowiedź na inne pytanie podobne do tego: Dlaczego można uzyskać dostęp do prywatnego elementu członkowskiego zagnieżdżonej klasy za pomocą metod klasy otaczającej?

Mówi się, że w JLS istnieje definicja prywatnego zakresu - Określanie dostępności :

W przeciwnym razie, jeśli element członkowski lub konstruktor zostanie zadeklarowany jako prywatny, dostęp jest dozwolony wtedy i tylko wtedy, gdy występuje w treści klasy najwyższego poziomu (§7.6), która zawiera deklarację elementu członkowskiego lub konstruktora.

Colin Su
źródło
Myślę, że ten fragment może ładnie odpowiedzieć na moje pytanie.
shen
5

Ważnym przypadkiem użycia IMHO dla klas wewnętrznych jest wzorzec fabryki. Klasa otaczająca może przygotować instancję klasy wewnętrznej bez ograniczeń dostępu i przekazać instancję do świata zewnętrznego, gdzie prywatny dostęp będzie honorowany.

W przeciwieństwie do abyx deklarowanie statycznej klasy nie zmienia ograniczeń dostępu do klasy otaczającej, jak pokazano poniżej. Działają również ograniczenia dostępu między klasami statycznymi w tej samej klasie otaczającej. Byłem zaskoczony ...

class MyPrivates {
    static class Inner1 { private int test1 = 2; }
    static class Inner2 { private int test2 = new Inner1().test1; }

    public static void main(String[] args) {
        System.out.println("Inner : "+new Inner2().test2);
    }
}
thomasfr
źródło
1
Miły komentarz, ale bez odpowiedzi.
ceving
@cerving To właściwie jedyna odpowiedź, która pozwoliła mi zobaczyć praktyczne, rzeczywiste zastosowanie tej skądinąd dziwacznej decyzji projektowej. Pytanie brzmiało, dlaczego tak zdecydowano, i to jest świetne rozumowanie - demonstracja różnicy między tym, do czego możesz chcieć, aby klasa zewnętrzna miała dostęp w klasie wewnętrznej, a do czego chcesz mieć dostęp inne niepowiązane klasy.
et_l
3

Ograniczenia dostępu są ustalane dla poszczególnych klas. Nie ma możliwości, aby metoda zadeklarowana w klasie nie mogła uzyskać dostępu do wszystkich elementów członkowskich instancji / klasy. Z tego wynika, że ​​klasy wewnętrzne również mają nieograniczony dostęp do członków klasy zewnętrznej, a klasa zewnętrzna ma nieograniczony dostęp do członków klasy wewnętrznej.

Umieszczając klasę w innej klasie, sprawiasz, że jest ona ściśle powiązana z implementacją, a wszystko, co jest częścią implementacji, powinno mieć dostęp do innych części.

TofuBeer
źródło
3

Logika stojąca za klasami wewnętrznymi jest taka, że ​​jeśli tworzysz klasę wewnętrzną w klasie zewnętrznej, dzieje się tak dlatego, że będą one musiały udostępniać kilka rzeczy, a zatem ma sens, aby mogły mieć większą elastyczność niż „zwykłe” klasy.

Jeśli w twoim przypadku nie ma sensu, aby klasy mogły widzieć swoje wewnętrzne działanie - co zasadniczo oznacza, że ​​klasa wewnętrzna mogła po prostu zostać utworzona jako zwykła klasa, możesz zadeklarować klasę wewnętrzną jako static class XYZ. Używanie staticbędzie oznaczać, że nie będą dzielić stanu (i na przykład new ABC().new XYZ()nie będą działać, a będziesz musiał użyć new ABC.XYZ().
Ale jeśli tak jest, powinieneś pomyśleć o tym, czy XYZnaprawdę powinna być klasą wewnętrzną i może zasługuje na swoją własną) Czasami sensowne jest utworzenie statycznej klasy wewnętrznej (na przykład, jeśli potrzebujesz małej klasy, która implementuje interfejs, z którego korzysta Twoja klasa zewnętrzna, i nie będzie to pomocne nigdzie indziej). Ale mniej więcej w połowie przypadków powinien był być klasą zewnętrzną.

otchłań
źródło
2
Klasa zewnętrzna może również uzyskać dostęp do prywatnych elementów członkowskich statycznych klas wewnętrznych, więc nie ma to nic wspólnego ze statycznymi . Mówisz: „nie ma sensu, aby klasy mogły widzieć swoje wewnętrzne mechanizmy”, ale niekoniecznie tak jest - co, jeśli tylko klasa wewnętrzna ma sens, aby zobaczyć wewnętrzne działanie klasy zewnętrznej, ale nie wady- versa?
LUB Mapper
3

Thilo dodał dobrą odpowiedź na Twoje pierwsze pytanie „Jak to możliwe?”. Chciałbym nieco rozwinąć drugie zadane pytanie: Dlaczego takie zachowanie jest dozwolone?

Na początek wyjaśnijmy, że to zachowanie nie ogranicza się do klas wewnętrznych, które z definicji są niestatycznymi typami zagnieżdżonymi. To zachowanie jest dozwolone dla wszystkich typów zagnieżdżonych, w tym zagnieżdżonych wyliczeń i interfejsów, które muszą być statyczne i nie mogą mieć obejmującego wystąpienia. Zasadniczo model jest uproszczeniem do następującej instrukcji: Zagnieżdżony kod ma pełny dostęp do otaczającego kodu - i odwrotnie.

Więc dlaczego? Myślę, że przykład lepiej to ilustruje.

Pomyśl o swoim ciele i mózgu. Jeśli wstrzykniesz heroinę w ramię, twój mózg się rozpali. Jeśli obszar ciała migdałowatego twojego mózgu zobaczy, co według niego stanowi zagrożenie dla twojego osobistego bezpieczeństwa, na przykład osa, sprawi, że twoje ciało obróci się w drugą stronę i pobiegnie w kierunku wzgórz bez Ciebie „zastanawiasz się” dwa razy o tym.

Mózg jest więc nieodłączną częścią ciała - i, co dziwne, także na odwrót. Korzystanie z kontroli dostępu między tak blisko powiązanymi podmiotami powoduje utratę praw do związku. Jeśli potrzebujesz kontroli dostępu, musisz bardziej rozdzielić klasy na naprawdę odrębne jednostki. Do tego czasu są tą samą jednostką. Dobrym przykładem dla dalszych badań byłoby przyjrzenie się, jak Iteratorzwykle implementowana jest Java .

Nieograniczony dostęp od kodu zamykającego do kodu zagnieżdżonego sprawia, że ​​w większości przypadków dodawanie modyfikatorów dostępu do pól i metod typu zagnieżdżonego jest raczej bezużyteczne. Takie postępowanie dodaje bałaganu i może zapewnić fałszywe poczucie bezpieczeństwa dla nowych użytkowników języka programowania Java.

Martin Andersson
źródło
1
To naprawdę powinna być akceptowana odpowiedź. Tak jasne i dokładne. Zamiast tego zaakceptowana odpowiedź nie odpowiada nawet na pytanie.
temporary_user_name
IMO to nadal nie daje odpowiedzi na pytanie, dlaczego nie możemy łatwo dodawać pól prywatnych do klasy wewnętrznej, do której klasa zewnętrzna nie ma bezpośredniego dostępu. O ile się nie mylę, rujnuje to jeden z podstawowych przypadków dla klas wewnętrznych - tworzenie krótkotrwałych, „strukturopodobnych”, niezmiennych typów. FWIW C # szczęśliwie to obsługuje: repl.it/repls/VengefulCheeryInverse
Josh M.
-1

Klasa wewnętrzna jest traktowana jako atrybut klasy Outer. Dlatego niezależnie od tego, czy zmienna instancji klasy Inner jest prywatna, czy nie, klasa Outer może uzyskać do niej dostęp bez żadnego problemu, tak jak dostęp do jej innych atrybutów prywatnych (zmiennych).

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}
MonMoonkey
źródło
-2

Ponieważ twoja main()metoda znajduje się w ABCklasie, która może uzyskać dostęp do własnej klasy wewnętrznej.

aberrant80
źródło
2
Pytanie nie dotyczy tego, czy członkowie ABCklasy mają dostęp do klas zagnieżdżonych w ABCklasie, ale dlaczego mają dostęp do prywatnych członków klas zagnieżdżonych w ABCklasie w Javie.
LUB Mapper
Odpowiedziałem na pytanie tego samego dnia, w którym zostało zadane. Ktoś zredagował pytanie 2 lata później, a głos przeciwny przyszedł 3 lata później. Jestem prawie pewien, że ktokolwiek zredagował pytanie, zmienił sformułowanie pytania za bardzo.
aberrant 80