Warum können äußere Java-Klassen auf private Mitglieder der inneren Klasse zugreifen?

177

Ich habe festgestellt, dass äußere Klassen auf private Instanzvariablen innerer Klassen zugreifen können. Wie ist das möglich? Hier ist ein Beispielcode, der dasselbe demonstriert:

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

Warum ist dieses Verhalten erlaubt?

Harish
quelle
Diese Frage hat mich eine ganze Weile verwirrt, bis ich den Kommentar sehe ... das erklärt, warum ich auf meinem Computer nicht auf xx.x zugreifen kann.
Wang Sheng
4
Die Kommentare haben mich verwirrt, ich führe den obigen Code in Java 8 aus, er kompiliert und läuft. Auf xx.x kann zugegriffen werden.
Leon
Harish, können Sie bitte die akzeptierte Antwort (die die von Ihnen gestellte Frage nicht beantwortet) ablehnen und stattdessen die Antwort von Martin Andersson unten akzeptieren, die sie sehr gründlich beantwortet?
temporärer_Benutzername
FWIW diese Syntax ist absolut horrend:new ABC().new XYZ()
Josh M.

Antworten:

79

Die innere Klasse ist nur eine Möglichkeit, einige Funktionen, die wirklich zur ursprünglichen äußeren Klasse gehören, sauber zu trennen. Sie sollen verwendet werden, wenn Sie zwei Anforderungen haben:

  1. Einige Funktionen in Ihrer äußeren Klasse wären am klarsten, wenn sie in einer separaten Klasse implementiert würden.
  2. Obwohl es sich um eine separate Klasse handelt, ist die Funktionalität sehr eng mit der Funktionsweise der äußeren Klasse verknüpft.

Angesichts dieser Anforderungen haben innere Klassen vollen Zugriff auf ihre äußere Klasse. Da sie im Grunde ein Mitglied der äußeren Klasse sind, ist es sinnvoll, dass sie Zugriff auf Methoden und Attribute der äußeren Klasse haben - einschließlich privater.

Kaleb Brasee
quelle
217
Diese Antwort erklärt, warum verschachtelte Klassen Zugriff auf private Mitglieder ihrer äußeren Klasse haben. Die Frage ist jedoch, warum die äußere Klasse Zugriff auf private Mitglieder verschachtelter Klassen hat.
Andrew
13
Fügen Sie einfach "und umgekehrt" zu hinzu. Angesichts dieser Anforderungen haben innere Klassen vollen Zugriff auf ihre äußere Klasse und beantworten nun die Frage.
Anthropomo
13
Dies ist nicht die richtige Antwort auf dieses Problem, hier tut: stackoverflow.com/questions/19747812/…
Colin Su
4
@anthropomo: Nein, das tut es nicht. Beide Anforderungen sind durchaus realisierbar, ohne dass die äußere Klasse Zugang zu privaten Mitgliedern der inneren Klasse hat.
ODER Mapper
Ein gutes Beispiel, bei dem dies besonders nützlich ist, ist das Builder-Muster stackoverflow.com/a/1953567/1262000 . Die übergeordnete Klasse benötigt nur einen Konstruktor, der einen Builder verwendet und auf alle seine Mitgliedsvariablen zugreift. Andernfalls benötigen Sie einen privaten Konstruktor in der übergeordneten Klasse mit allen privaten Mitgliedsvariablen.
vikky.rk
62

Wenn Sie die privaten Mitglieder Ihrer inneren Klasse ausblenden möchten, können Sie eine Schnittstelle mit den öffentlichen Mitgliedern definieren und eine anonyme innere Klasse erstellen, die diese Schnittstelle implementiert. Beispiel unten:

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
quelle
Dies ist ein brillanter Codeausschnitt. Genau das, was ich brauchte. Vielen Dank!
Kevinarpe
Bitte geben Sie den Code an, der ausgeführt werden kann. Was mehr ist, bitte der Grund, warum die private Variable nicht zugreifen durfte.
Androidyue
7
Aber dann ... ist die innere Klasse anonym. Sie können nicht mehrere Instanzen dieser inneren Klasse erstellen oder diese innere Klasse für Variablendeklarationen usw. verwenden.
ODER Mapper
@OR Mapper, deshalb ist es auch dann xnicht erlaubt, wenn es hier öffentlich ist mMember.x.
MAC
53

Die innere Klasse wird (zum Zwecke der Zugriffskontrolle) als Teil der enthaltenden Klasse betrachtet. Dies bedeutet vollen Zugriff auf alle Privaten.

Die Implementierung erfolgt mithilfe synthetischer paketgeschützter Methoden: Die innere Klasse wird in einer separaten Klasse im selben Paket (ABC $ XYZ) kompiliert. Die JVM unterstützt diese Isolationsstufe nicht direkt, sodass ABC $ XYZ auf Bytecode-Ebene über paketgeschützte Methoden verfügt, mit denen die äußere Klasse zu den privaten Methoden / Feldern gelangt.

Thilo
quelle
17

Auf eine ähnliche Frage wird eine richtige Antwort angezeigt: Warum kann auf das private Mitglied einer verschachtelten Klasse mit den Methoden der einschließenden Klasse zugegriffen werden?

Es heißt, es gibt eine Definition des privaten Scoping in JLS - Ermittlung der Zugänglichkeit :

Andernfalls, wenn das Mitglied oder der Konstruktor als privat deklariert ist, ist der Zugriff nur dann zulässig, wenn er innerhalb des Hauptteils der Klasse der obersten Ebene (§7.6) erfolgt, der die Deklaration des Mitglieds oder Konstruktors enthält.

Colin Su
quelle
Ich denke, dieser Ausschnitt kann meine Frage gut beantworten.
Shen
5

Ein meiner Meinung nach wichtiger Anwendungsfall für innere Klassen ist das Fabrikmuster. Die einschließende Klasse kann eine Instanz der inneren Klasse ohne Zugriffsbeschränkungen vorbereiten und die Instanz an die Außenwelt weitergeben, wo der private Zugriff anerkannt wird.

Im Gegensatz zu deyx, das die Klasse statisch deklariert, werden die Zugriffsbeschränkungen für die einschließende Klasse nicht geändert, wie unten gezeigt. Auch die Zugriffsbeschränkungen zwischen statischen Klassen in derselben umschließenden Klasse funktionieren. Ich war überrascht ...

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
quelle
1
Netter Kommentar, aber keine Antwort.
Ceving
@cerving Dies ist eigentlich die einzige Antwort, die es mir ermöglichte, eine praktische reale Verwendung dieser ansonsten bizarren Designentscheidung zu sehen. Die Frage war, warum dies so entschieden wurde, und dies ist eine gute Argumentation - eine Demonstration des Unterschieds zwischen dem, worauf die äußere Klasse in der inneren Klasse zugreifen soll, und dem, worauf andere nicht verwandte Klassen zugreifen sollen.
et_l
3

Zugriffsbeschränkungen werden pro Klasse vorgenommen. Es gibt keine Möglichkeit für eine in einer Klasse deklarierte Methode, nicht auf alle Instanz- / Klassenmitglieder zuzugreifen. Es liegt auf der Hand, dass innere Klassen auch uneingeschränkten Zugang zu den Mitgliedern der äußeren Klasse haben und die äußere Klasse ungehinderten Zugang zu den Mitgliedern der inneren Klasse hat.

Indem Sie eine Klasse in eine andere Klasse einfügen, ist sie eng mit der Implementierung verbunden, und alles, was Teil der Implementierung ist, sollte Zugriff auf die anderen Teile haben.

TofuBeer
quelle
3

Die Logik hinter inneren Klassen besteht darin, dass, wenn Sie eine innere Klasse in einer äußeren Klasse erstellen, diese einige Dinge gemeinsam nutzen müssen und es daher sinnvoll ist, dass sie flexibler sind als "normale" Klassen.

Wenn es in Ihrem Fall keinen Sinn macht, dass die Klassen das Innenleben des anderen sehen können - was im Grunde bedeutet, dass die innere Klasse einfach zu einer regulären Klasse hätte gemacht werden können, können Sie die innere Klasse als deklarieren static class XYZ. Verwenden staticbedeutet, dass sie keinen gemeinsamen Status haben (und beispielsweise new ABC().new XYZ()nicht funktionieren, und Sie müssen ihn verwenden new ABC.XYZ().
Wenn dies jedoch der Fall ist, sollten Sie darüber nachdenken, ob es XYZsich wirklich um eine innere Klasse handelt und ob sie möglicherweise ihren eigenen verdient Manchmal ist es sinnvoll, eine statische innere Klasse zu erstellen (z. B. wenn Sie eine kleine Klasse benötigen, die eine Schnittstelle implementiert, die Ihre äußere Klasse verwendet, und die sonst nirgendwo hilfreich ist). Aber in etwa der Hälfte der Fälle es sollte eine äußere Klasse gemacht worden sein.

Abyx
quelle
2
Die äußere Klasse kann genauso gut auf die privaten Mitglieder statischer innerer Klassen zugreifen , dies hat also nichts mit statischer zu tun . Sie sagen, "es macht keinen Sinn, dass die Klassen das Innenleben des anderen sehen können", aber das ist nicht unbedingt der Fall - was ist, wenn es nur für die innere Klasse Sinn macht, das Innenleben der äußeren Klasse zu sehen, aber nicht umgekehrt? umgekehrt?
ODER Mapper
3

Thilo hat eine gute Antwort auf Ihre erste Frage "Wie ist das möglich?" Hinzugefügt . Ich möchte auf die zweite gestellte Frage etwas näher eingehen: Warum ist dieses Verhalten zulässig?

Lassen Sie uns zunächst klarstellen, dass dieses Verhalten nicht auf innere Klassen beschränkt ist, die per Definition nicht statische verschachtelte Typen sind. Dieses Verhalten ist für alle verschachtelten Typen zulässig, einschließlich verschachtelter Aufzählungen und Schnittstellen, die statisch sein müssen und keine umschließende Instanz haben dürfen. Grundsätzlich ist das Modell eine Vereinfachung bis auf die folgende Aussage: Verschachtelter Code hat vollen Zugriff auf umschließenden Code - und umgekehrt.

Warum also? Ich denke, ein Beispiel veranschaulicht den Punkt besser.

Denken Sie an Ihren Körper und Ihr Gehirn. Wenn Sie Heroin in Ihren Arm injizieren, wird Ihr Gehirn hoch. Wenn die Amygdala-Region Ihres Gehirns erkennt, was seiner Meinung nach eine Bedrohung für Ihre persönliche Sicherheit darstellt, beispielsweise eine Wespe, wird er Ihren Körper dazu bringen, sich umzudrehen und auf die Hügel zu rennen, ohne dass Sie zweimal darüber nachdenken.

Das Gehirn ist also ein wesentlicher Bestandteil des Körpers - und seltsamerweise auch umgekehrt. Durch die Verwendung der Zugriffskontrolle zwischen solchen eng verbundenen Unternehmen verfällt ihr Anspruch auf Beziehung. Wenn Sie eine Zugriffskontrolle benötigen, müssen Sie die Klassen stärker in wirklich unterschiedliche Einheiten unterteilen. Bis dahin sind sie die gleiche Einheit. Ein treibendes Beispiel für weitere Studien wäre, zu untersuchen, wie ein Java Iteratornormalerweise implementiert wird.

Der uneingeschränkte Zugriff von eingeschlossenem Code auf verschachtelten Code macht es größtenteils ziemlich nutzlos, Zugriffsmodifikatoren zu Feldern und Methoden eines verschachtelten Typs hinzuzufügen. Dies führt zu Unordnung und kann für Neueinsteiger der Java-Programmiersprache ein falsches Sicherheitsgefühl darstellen.

Martin Andersson
quelle
1
Dies sollte wirklich die akzeptierte Antwort sein. So klar und gründlich. Stattdessen geht die akzeptierte Antwort nicht einmal auf die Frage ein.
temporärer_Benutzername
IMO beantwortet dies immer noch nicht die Frage, warum wir einer inneren Klasse, auf die die äußere Klasse nicht direkt zugreifen kann, nicht einfach private Felder hinzufügen können. Wenn ich mich nicht irre, ruiniert dies einen der Hauptfälle für innere Klassen - die Schaffung kurzlebiger "strukturartiger", unveränderlicher Typen. FWIW C # unterstützt dies gerne: repl.it/repls/VengefulCheeryInverse
Josh M.
-1

Die innere Klasse wird als Attribut der äußeren Klasse angesehen. Unabhängig davon, ob die Instanzvariable der inneren Klasse privat ist oder nicht, kann die äußere Klasse problemlos auf sie zugreifen, genau wie auf ihre anderen privaten Attribute (Variablen).

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}
MonMoonkey
quelle
-2

Weil sich Ihre main()Methode in der ABCKlasse befindet, die auf ihre eigene innere Klasse zugreifen kann .

aberrant80
quelle
2
Die Frage ist nicht, ob Mitglieder der ABCKlasse auf in der ABCKlasse verschachtelte Klassen zugreifen können , sondern warum sie auf private Mitglieder von Klassen zugreifen können, die in der ABCKlasse in Java verschachtelt sind .
ODER Mapper
Ich beantwortete die Frage am selben Tag, an dem sie gestellt wurde. Jemand hat die Frage 2 Jahre später bearbeitet, und die Ablehnung erfolgte 3 Jahre später. Ich bin mir ziemlich sicher, wer auch immer die Frage bearbeitet hat, hat den Wortlaut der Frage viel zu sehr geändert.
aberrant80