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

Zauważyłem, że klasy zewnętrzne mają dostęp do zmiennych instancji prywatnych klas wewnętrznych. Jak to 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?
Author: Paŭlo Ebermann, 2009-11-26

10 answers

Klasa wewnętrzna jest tylko sposobem na czyste oddzielenie niektórych funkcji, które naprawdę należą do oryginalnej klasy zewnętrznej. Są one przeznaczone do stosowania, gdy masz 2 Wymagania:

  1. jakaś funkcjonalność w twojej zewnętrznej klasie byłaby najbardziej jasna, gdyby została zaimplementowana w oddzielnej klasie.
  2. mimo że jest to oddzielna Klasa, funkcjonalność jest bardzo ściśle powiązana ze sposobem działania klasy zewnętrznej.

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

 78
Author: Kaleb Brasee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-26 05:52:13

Jeśli chcesz ukryć członków prywatnych swojej wewnętrznej klasy, 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
    }
}
 53
Author: Ich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-22 21:09:58

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

Sposób w jaki jest to zaimplementowane jest przy użyciu syntetycznych metod chronionych pakietami: Klasa wewnętrzna zostanie skompilowana do oddzielnej klasy w tym samym pakiecie (ABC$XYZ). JVM nie obsługuje bezpośrednio tego poziomu izolacji, tak więc na poziomie kodu bajtowego ABC$XYZ będzie miała metody chronione pakietem, których zewnętrzna Klasa używa, aby dostać się do prywatnego metody/pola.

 48
Author: Thilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-26 06:01:18

Na innym pytaniu podobnym do tego pojawia się poprawna odpowiedź: Dlaczego prywatny członek zagnieżdżonej klasy może być dostępny za pomocą metod klasy zamykającej?

Mówi, że istnieje definicja prywatnego zakresu na JLS-określająca dostępność :

W przeciwnym razie, jeśli członek lub konstruktor jest zadeklarowany jako prywatny, wtedy dostęp jest dozwolony wtedy i tylko wtedy, gdy występuje w ciele klasy najwyższego poziomu (§7.6), która obejmuje deklaracja członka lub konstruktora.

 14
Author: Colin Su,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:36

IMHO ważnym przypadkiem użycia klas wewnętrznych jest wzorzec fabryczny. Klasa 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 sprzeczności zabyx deklarowanie klasy static nie zmienia ograniczeń dostępu do klasy zamkniętej, jak pokazano poniżej. Działają również ograniczenia dostępu między klasami statycznymi w tej samej klasie. 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);
    }
}
 4
Author: thomasfr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:36

Ograniczenia dostępu są dokonywane w zależności od klasy. Nie ma możliwości, aby metoda zadeklarowana w klasie nie mogła uzyskać dostępu do wszystkich członków instancji/klasy. Wynika to z tego, że klasy wewnętrzne również mają nieskrępowany dostęp do członków klasy zewnętrznej, a klasa zewnętrzna ma nieskrępowany dostęp do członków klasy wewnętrznej.

Umieszczając klasę wewnątrz innej klasy, czynisz ją ściśle powiązaną z implementacją, a wszystko, co jest częścią wdrożenie powinno mieć dostęp do pozostałych części.

 3
Author: TofuBeer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-26 06:27:39

Logika stojąca za klasami wewnętrznymi jest taka, że jeśli tworzysz klasę wewnętrzną w klasie zewnętrznej, to dlatego, że będą one musiały dzielić się kilkoma rzeczami, a zatem ma to sens, aby mogły mieć większą elastyczność niż" zwykłe " klasy.

Jeśli, w Twoim przypadku, nie ma sensu, aby klasy były w stanie widzieć swoje wewnętrzne działanie - co w zasadzie oznacza, że Klasa wewnętrzna mogła być po prostu zwykłą klasą, możesz zadeklarować klasę wewnętrzną jako static class XYZ. Using static oznacza to, że nie będą udostępniać stanu (i na przykład new ABC().new XYZ() nie będą działać, i będziesz musiał użyć new ABC.XYZ().
Ale jeśli tak jest, powinieneś pomyśleć o tym, czy XYZ naprawdę powinna być klasą wewnętrzną i że może zasługuje na swój własny plik. Czasami warto stworzyć statyczną klasę wewnętrzną (na przykład, jeśli potrzebujesz małej klasy, która implementuje interfejs, którego używa twoja klasa zewnętrzna, a to nie będzie pomocne nigdzie indziej). Ale w około połowie czasu powinno się zrobić Klasa zewnętrzna.

 3
Author: abyx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-26 06:42:18

Thilo dodał dobrą Odpowiedź na twoje pierwsze pytanie "Jak to możliwe?". Chciałbym rozwinąć trochę na drugim zadanym pytaniu: dlaczego takie zachowanie jest dozwolone?

Na początek wyjaśnijmy sobie, że takie zachowanie jest dozwolone nie tylko dla klas wewnętrznych, które z definicji są niestatycznymi typami zagnieżdżonymi. Takie zachowanie jest dozwolone dla wszystkich zagnieżdżonych typów, w tym zagnieżdżonych enum i interfejsów, które muszą być statyczne i nie mogą mieć zamykającej się instancji. Zasadniczo, model jest uproszczeniem do następującego stwierdzenia: zagnieżdżony kod ma pełny dostęp do kodu-i vice versa.

Więc dlaczego? Myślę, że przykład lepiej ilustruje ten punkt. Pomyśl o swoim ciele i mózgu. Jeśli wstrzykniesz heroinę w ramię, twój mózg się podnieci. Jeśli obszar ciała migdałowatego twojego mózgu zobaczy, co uważa za zagrożenie dla Twojego osobistego bezpieczeństwa, na przykład osa, sprawi, że twoje ciało odwróci się na odwrót i ucieknie w góry bez "Myślisz" o tym dwa razy. Więc mózg jest nieodłączną częścią ciała - i co dziwne, na odwrót. Korzystanie z kontroli dostępu między takimi blisko powiązanymi podmiotami traci swoje roszczenie o związek. Jeśli potrzebujesz kontroli dostępu, musisz rozdzielić klasy bardziej na prawdziwie odrębne jednostki. Do tego czasu to ta sama jednostka. Przykładem do dalszych badań byłoby przyjrzenie się, jak Java Iterator zwykle jest wdrożone.

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

 1
Author: Martin Andersson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:46

Klasa wewnętrzna jest uważana za atrybut klasy zewnętrznej. Dlatego, bez względu na to, czy wewnętrzna instancja klasy jest prywatna, czy nie, klasa zewnętrzna może uzyskać dostęp bez żadnego problemu, tak jak dostęp do innych prywatnych atrybutów (zmiennych).

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}
 -1
Author: MonMoonkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-25 03:13:13

Ponieważ twoja metoda main() jest w klasie ABC, Która Może uzyskać dostęp do własnej klasy wewnętrznej.

 -2
Author: aberrant80,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-26 05:49:26