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


Ho osservato che le classi esterne possono accedere alle variabili di istanza private 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é questo comportamento è consentito?

Author: Paŭlo Ebermann, 2009-11-26

10 answers

La classe interna è solo un modo per separare in modo pulito alcune funzionalità che appartengono realmente alla classe esterna originale. Sono destinati ad essere utilizzati quando si hanno 2 requisiti:

  1. Alcune funzionalità nella classe esterna sarebbero più chiare se fossero implementate in una classe separata.
  2. Anche se si trova 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 un membro della classe outer, ha senso che abbiano accesso ai metodi e agli attributi della classe outer, inclusi i privati.

 78
Author: Kaleb Brasee, 2009-11-26 05:52:13

Se vuoi nascondere i membri privati della tua classe interna, puoi definire un'Interfaccia con i membri pubblici e creare una classe interna anonima che implementi questa interfaccia. Esempio qui 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
    }
}
 53
Author: Ich, 2018-08-22 21:09:58

La classe interna (ai fini del controllo degli accessi) è considerata parte della classe contenente. Ciò significa pieno accesso a tutti i privati.

Il modo in cui questo viene implementato utilizza metodi sintetici protetti da pacchetti: la classe interna verrà compilata in una classe separata nello stesso pacchetto (ABC XY XYZ). La JVM non supporta direttamente questo livello di isolamento, in modo che a livello di bytecode ABC XY XYZ abbia metodi protetti dai pacchetti che la classe esterna utilizza per raggiungere il privato metodi/campi.

 48
Author: Thilo, 2009-11-26 06:01:18

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

Dice che esiste una definizione di ambito privato su Accessibilità che determina JLS :

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

 14
Author: Colin Su, 2017-05-23 12:26:36

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 l'accesso privato sarà onorato.

In contraddizione con abyx dichiarare la classe static non modifica le restrizioni di accesso alla classe che racchiude, come mostrato di seguito. Anche le restrizioni di accesso tra le classi statiche nella stessa classe che racchiude stanno funzionando. Ero sorprendere ...

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, 2017-05-23 12:26:36

Le restrizioni di accesso vengono eseguite 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 le classi interne hanno anche accesso illimitato ai membri della classe esterna, e la classe esterna ha accesso illimitato ai membri della classe interna.

Inserendo una classe all'interno di un'altra classe, la stai rendendo strettamente legata all'implementazione e a tutto ciò che fa parte del l'attuazione dovrebbe avere accesso alle altre parti.

 3
Author: TofuBeer, 2009-11-26 06:27:39

La logica alla base delle classi interne è che se crei una classe interna in una classe esterna, è perché avranno bisogno di condividere alcune cose, e quindi ha senso per loro essere in grado di avere più flessibilità rispetto alle classi "regolari".

Se, nel tuo caso, non ha senso che le classi siano in grado di vedere il funzionamento interno dell'altro - il che significa fondamentalmente che la classe interna potrebbe semplicemente essere stata resa una classe normale, puoi dichiarare la classe interna come static class XYZ. Utilizzo di static significherà che non condivideranno lo stato (e, ad esempio new ABC().new XYZ() non funzionerà, e dovrai usare new ABC.XYZ().
Ma, se questo è il caso, dovresti pensare se XYZ dovrebbe davvero essere una classe interna e che forse merita il proprio file. A volte ha senso creare una classe interna statica (ad esempio, se hai bisogno di una piccola classe che implementi un'interfaccia che la tua classe esterna sta usando e che non sarà utile da nessun'altra parte). Ma a circa la metà del tempo avrebbe dovuto essere fatto un classe esterna.

 3
Author: abyx, 2009-11-26 06:42:18

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

Per cominciare, siamo perfettamente chiari che questo comportamento non è consentito solo per le classi interne che per definizione sono tipi nidificati non statici. Questo comportamento è consentito per tutti i tipi nidificati, comprese le enumerazioni nidificate e le interfacce che devono essere statiche e non possono contenere un'istanza di inclusione. Fondamentalmente, il il modello è una semplificazione fino alla seguente istruzione: il codice nidificato ha pieno accesso al codice che racchiude e viceversa.

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

Pensa al tuo corpo e al tuo cervello. Se ti inietti eroina nel braccio, il tuo cervello diventa alto. 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 dall'altra parte e correrà per le colline senza Ci hai "pensato" due volte.

Quindi, il cervello è una parte intrinseca del corpo - e stranamente, anche il contrario. L'utilizzo del controllo degli accessi tra tali entità strettamente correlate perde la loro pretesa di relazione. Se hai bisogno del controllo degli accessi, devi separare le classi più in unità veramente distinte. Fino ad allora, sono la stessa unità. Un esempio guida per ulteriori studi sarebbe quello di guardare come un Java Iterator di solito è attuare.

L'accesso illimitato dal codice che racchiude 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
Author: Martin Andersson, 2017-05-23 12:02:46

La classe interna è considerata come un attributo della classe Esterna. Pertanto, indipendentemente dal fatto che la variabile di istanza della classe interna sia privata o meno, la classe esterna può accedere senza alcun problema proprio come l'accesso agli altri attributi privati(variabili).

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}
 -1
Author: MonMoonkey, 2018-04-25 03:13:13

Perché il tuo metodo main() si trova nella classe ABC, che può accedere alla propria classe interna.

 -2
Author: aberrant80, 2009-11-26 05:49:26