Perché le classi Java esterne possono accedere ai membri privati ​​della classe interna?


177

Ho osservato che le classi esterne possono accedere alle variabili di istanza privata delle classi interne. Com'è possibile? Ecco un codice di esempio che dimostra lo stesso:

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

Perché è consentito questo comportamento?


Questa domanda mi ha confuso per un bel po 'fino a quando non vedo il commento ... questo spiega perché non riesco ad accedere a xx.x sulla mia macchina ..
Wang Sheng

4
I commenti mi hanno confuso, ho eseguito il codice sopra in Java 8, si compila ed esegue. È possibile accedere a xx.x.
leon,

Harish, puoi per favore non accettare la risposta accettata (che non risponde alla domanda che hai posto) e invece accettare la risposta di Martin Andersson in basso, che risponde molto a fondo?
temporaneo

FWIW questa sintassi è assolutamente orribile:new ABC().new XYZ()
Josh M.

Risposte:


80

La classe interna è solo un modo per separare in modo pulito alcune funzionalità che appartengono davvero alla classe esterna originale. Sono destinati all'uso quando si hanno 2 requisiti:

  1. Alcune funzionalità nella tua classe esterna sarebbero più chiare se fosse implementata in una classe separata.
  2. Anche se è in una classe separata, la funzionalità è strettamente legata al modo in cui funziona la classe esterna.

Dati questi requisiti, le classi interne hanno pieno accesso alla loro classe esterna. Dal momento che sono fondamentalmente membri della classe esterna, ha senso che abbiano accesso a metodi e attributi della classe esterna - compresi i privati.


217
Questa risposta spiega perché le classi nidificate hanno accesso ai membri privati ​​della loro classe esterna. Ma la domanda è: perché la classe esterna ha accesso ai membri privati ​​delle classi nidificate.
Andrew,

13
Basta aggiungere "e viceversa" a Dati questi requisiti, le classi interne hanno pieno accesso alla loro classe esterna e ora risponde alla domanda.
anthropomo,

13
Questa non è la risposta corretta a questo problema, qui: stackoverflow.com/questions/19747812/…
Colin Su

4
@anthropomo: No, non lo è. Entrambi i requisiti sono perfettamente fattibili senza che la classe esterna abbia accesso ai membri privati ​​delle classi interne.
OR Mapper

Un buon esempio in cui ciò è particolarmente utile è il Builder Pattern, stackoverflow.com/a/1953567/1262000 . La classe genitore ha solo bisogno di un costruttore che prenda un Builder e acceda a tutte le sue variabili membro. Altrimenti dovresti avere un costruttore privato nella classe genitore con tutte le variabili del membro privato.
vikky.rk,

62

Se ti piace nascondere i membri privati ​​della tua classe interna, puoi definire un'interfaccia con i membri pubblici e creare una classe interna anonima che implementa questa interfaccia. Esempio sotto:

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

Questo è un brillante frammento di codice. Proprio quello di cui avevo bisogno. Grazie!
kevinarpe l'

Fornire il codice che potrebbe essere in grado di eseguire. Inoltre, per favore, non è possibile accedere alla variabile privata.
androidyue,

7
Ma poi ... la classe interna è anonima. Non è possibile creare diverse istanze di quella classe interna o utilizzare quella classe interna per dichiarazioni di variabili ecc.
OR Mapper

@OR Mapper ecco perché anche se xè pubblico qui, non è permesso mMember.x.
MAC

54

La classe interna è (ai fini del controllo di accesso) considerata parte della classe di contenimento. Questo significa pieno accesso a tutti i privati.

Il modo in cui questo viene implementato sta usando metodi sintetici protetti da pacchetti: la classe interna verrà compilata in una classe separata nello stesso pacchetto (ABC $ XYZ). La JVM non supporta direttamente questo livello di isolamento, quindi a livello di codice byte ABC $ XYZ avrà metodi protetti dal pacchetto che la classe esterna usa per accedere ai metodi / campi privati.


17

C'è una risposta corretta che appare su un'altra domanda simile a questa: Perché è possibile accedere al membro privato di una classe nidificata con i metodi della classe che la racchiude?

Dice che esiste una definizione di ambito privato su JLS - Determinazione dell'accessibilità :

Altrimenti, se il membro o il costruttore viene dichiarato privato, l' accesso è consentito se e solo se si verifica all'interno del corpo della classe di livello superiore (§7.6) che racchiude la dichiarazione del membro o del costruttore.


Penso che questo frammento possa rispondere bene alla mia domanda.
shen

5

Un caso d'uso IMHO importante per le classi interne è il modello di fabbrica. La classe che racchiude può preparare un'istanza della classe interna senza restrizioni di accesso e passare l'istanza al mondo esterno, dove verrà onorato l'accesso privato.

In contraddizione con l' abisso dichiara la classe statica non cambia le restrizioni di accesso alla classe che la racchiude, come mostrato di seguito. Anche le restrizioni di accesso tra le classi statiche nella stessa classe chiusa funzionano. Ero sorpreso ...

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

1
Bel commento ma nessuna risposta.
va il

@cerving Questa è in realtà l'unica risposta che mi ha permesso di vedere un reale utilizzo pratico di questa decisione di design altrimenti bizzarra. La domanda era perché è stato deciso in questo modo, e questo è un grande ragionamento - una dimostrazione della differenza tra ciò che si potrebbe desiderare di accedere alla classe esterna nella classe interna e ciò a cui si desidera accedere ad altre classi non correlate.
et_l

3

Le restrizioni di accesso vengono effettuate in base alla classe. Non è possibile che un metodo dichiarato in una classe non sia in grado di accedere a tutti i membri dell'istanza / classe. È ovvio che anche le classi interne hanno accesso illimitato ai membri della classe esterna e la classe esterna ha accesso illimitato ai membri della classe interna.

Inserendo una classe in un'altra classe la rendi strettamente legata all'implementazione e tutto ciò che fa parte dell'implementazione dovrebbe avere accesso alle altre parti.


3

La logica alla base delle classi interne è che se si crea una classe interna in una classe esterna, ciò è dovuto al fatto che dovranno condividere alcune cose, e quindi ha senso per loro avere maggiore flessibilità rispetto alle classi "normali".

Se, nel tuo caso, non ha senso che le classi siano in grado di vedere i reciproci meccanismi interni - il che significa sostanzialmente che la classe interna avrebbe potuto semplicemente essere una classe regolare, puoi dichiarare la classe interna come static class XYZ. L'uso staticsignificherà che non condivideranno lo stato (e, per esempio new ABC().new XYZ(), non funzionerà, e dovrai usarlo new ABC.XYZ().
Ma, in tal caso, dovresti pensare se XYZdovrebbe davvero essere una classe interiore e che forse merita la sua A volte ha senso creare una classe interna statica (ad esempio, se hai bisogno di una piccola classe che implementa un'interfaccia utilizzata dalla tua classe esterna e che non sarà utile in nessun altro luogo). Ma circa la metà delle volte avrebbe dovuto diventare una classe esterna.


2
Anche la classe esterna può accedere ai membri privati delle classi interne statiche , quindi questo non ha nulla a che fare con l' elettricità statica . Dici "non ha senso che le classi siano in grado di vedere i reciproci meccanismi interni", ma non è necessariamente così - e se avesse senso solo per la classe interna vedere i meccanismi interni della classe esterna, ma non vice- versa?
OR Mapper

3

Thilo ha aggiunto una buona risposta per la tua prima domanda "Com'è possibile?". Vorrei approfondire un po 'la seconda domanda: perché questo comportamento è permesso?

Per cominciare, chiariamo perfettamente che questo comportamento non è limitato alle classi interne, che per definizione sono tipi nidificati non statici. Questo comportamento è consentito per tutti i tipi nidificati, inclusi enumerazioni e interfacce nidificate che devono essere statiche e non possono avere un'istanza chiusa. Fondamentalmente, il modello è una semplificazione fino alla seguente dichiarazione: il codice nidificato ha pieno accesso al codice che lo racchiude - e viceversa.

Allora perché allora? Penso che un esempio illustri meglio il punto.

Pensa al tuo corpo e al tuo cervello. Se iniettate eroina nel braccio, il cervello si alza. Se la regione dell'amigdala del tuo cervello vede ciò che crede sia una minaccia per la tua sicurezza personale, ad esempio una vespa, farà girare il tuo corpo al contrario e correrà verso le colline senza che tu "ci pensi" due volte.

Quindi, il cervello è una parte intrinseca del corpo - e stranamente, anche il contrario. L'uso del controllo dell'accesso tra entità così strettamente correlate perde la loro pretesa di relazione. Se è necessario il controllo dell'accesso, è necessario separare le classi in unità veramente distinte. Fino ad allora, sono la stessa unità. Un esempio determinante per ulteriori studi sarebbe quello di osservare come Iteratorviene solitamente implementato un Java .

L'accesso illimitato dal codice racchiuso al codice nidificato rende, per la maggior parte, piuttosto inutile aggiungere modificatori di accesso a campi e metodi di tipo nidificato. In questo modo si aggiunge disordine e potrebbe fornire un falso senso di sicurezza per i nuovi arrivati ​​del linguaggio di programmazione Java.


1
Questa dovrebbe davvero essere la risposta accettata. Così chiaro e completo. Invece la risposta accettata non risponde nemmeno alla domanda.
temporaneo

L'IMO non risponde ancora alla domanda sul perché non possiamo facilmente aggiungere campi privati ​​a una classe interna, alla quale la classe esterna non può accedere direttamente. A meno che non mi sbagli, questo rovina uno dei casi primari per le classi interiori: la creazione di tipi "strutturalmente" di breve durata, immutabili. FWIW C # supporta felicemente questo: repl.it/repls/VengefulCheeryInverse
Josh M.

-1

La classe interna è considerata come un attributo della classe esterna. Pertanto, indipendentemente dal fatto che la variabile dell'istanza della classe Inner sia privata o meno, la classe Outer può accedere senza problemi proprio come accedere agli altri suoi attributi privati ​​(variabili).

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}

-2

Perché il tuo main()metodo è nella ABCclasse, che può accedere alla propria classe interna.


2
La domanda non è se i membri della ABCclasse possano accedere alle classi nidificate nella ABCclasse, ma perché possano accedere ai membri privati delle classi nidificate nella ABCclasse in Java.
OR Mapper

Ho risposto alla domanda lo stesso giorno in cui è stata posta. Qualcuno ha modificato la domanda 2 anni dopo e il downvote è arrivato 3 anni dopo. Sono abbastanza sicuro che chiunque abbia modificato la domanda abbia cambiato completamente la formulazione della domanda.
Aberrant80
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.