Domanda

Non capisco perché questo venga compilato.f() e g() sono visibili dalle classi interne, nonostante siano private.Sono trattati in modo speciale perché appartengono alle classi interne?

Se A e B non sono classi statiche, è sempre la stessa cosa.

class NotPrivate {
    private static class A {
        private void f() {
            new B().g();
        }
    }

    private static class B {
        private void g() {
            new A().f();
        }
    }
}
È stato utile?

Soluzione

(Modificare:ampliato la risposta per rispondere ad alcuni commenti)

Il compilatore prende le classi interne e le trasforma in classi di primo livello.Poiché i metodi privati ​​sono disponibili solo per la classe interna, il compilatore deve aggiungere nuovi metodi "sintetici" che abbiano accesso a livello di pacchetto in modo che le classi di livello superiore possano accedervi.

Qualcosa del genere (quelli $ vengono aggiunti dal compilatore):

class A 
{
    private void f() 
    {
        final B b;

        b = new B();

        // call changed by the compiler
        b.$g();
    }

    // method generated by the compiler - visible by classes in the same package
    void $f()
    {
        f();
    }
}

class B
{
    private void g() 
    {
        final A a;

        a = new A();

        // call changed by the compiler
        a.$f();
    }

    // method generated by the compiler - visible by classes in the same package
    void $g()
    {
        g();
    }
}

Le classi non statiche sono le stesse, ma hanno l'aggiunta di un riferimento alla classe esterna in modo che i metodi possano essere richiamati su di essa.

Il motivo per cui Java lo fa in questo modo è che non volevano richiedere modifiche alla VM per supportare le classi interne, quindi tutte le modifiche dovevano essere a livello di compilatore.

Il compilatore prende la classe interna e la trasforma in una classe di livello superiore (quindi, a livello di VM non esiste una classe interna).Il compilatore deve quindi generare anche i nuovi metodi di "inoltro".Sono realizzati a livello di pacchetto (non pubblico) per garantire che solo le classi nello stesso pacchetto possano accedervi.Il compilatore ha inoltre aggiornato le chiamate ai metodi privati ​​nei metodi di "inoltro" generati.

Puoi evitare che il compilatore generi il metodo dichiarando i metodi come "pacchetto" (l'assenza di pubblico, privato e protetto).Lo svantaggio è che qualsiasi classe nel pacchetto può chiamare i metodi.

Modificare:

Sì, puoi chiamare il metodo generato (sintetico), ma NON FARE QUESTO!:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Main
{
    public static void main(final String[] argv)
        throws Exception
    {
        final Class<?> clazz;

        clazz = Class.forName("NotPrivate$A");        

        for(final Method method : clazz.getDeclaredMethods())
        {
            if(method.isSynthetic())
            {
                final Constructor constructor;
                final Object instance;

                constructor = clazz.getDeclaredConstructor(new Class[0]);
                constructor.setAccessible(true);
                instance = constructor.newInstance();
                method.setAccessible(true);
                method.invoke(null, instance);
            }
        }
    }
}

Altri suggerimenti

Penso che questa citazione lo riassume bene :

... le classi interne possono accedere a tutti i membri della classe dichiarante, anche ai membri privati. In effetti, si dice che la stessa classe interna sia un membro della classe; pertanto, seguendo le regole dell'ingegneria orientata agli oggetti, dovrebbe avere accesso a tutti i membri della classe.

E a seguito di ciò, poiché entrambe le classi interne sono in realtà solo parte della classe contenitrice, dovrebbero essere in grado di accedere anche agli altri membri privati.

Java viene compilato in accessor speciali con $ in essi. Quindi non puoi scrivere Java che accedono ai metodi privati. Spiegato qui:

http://www.retrologic.com/innerclasses.doc7.html

  

Esiste un'altra categoria di membri generati dal compilatore. Un membro privato m di una classe C può essere utilizzato da un'altra classe D, se una classe racchiude l'altra o se sono racchiuse da una classe comune. Poiché la macchina virtuale non è a conoscenza di questo tipo di raggruppamento, il compilatore crea un protocollo locale di metodi di accesso in C per consentire a D di leggere, scrivere o chiamare il membro m. Questi metodi hanno nomi del modulo accesso $ 0, accesso $ 1, ecc. Non sono mai pubblici. I metodi di accesso sono unici in quanto possono essere aggiunti alle classi che racchiudono, non solo alle classi interne.

Come ha spiegato l'utente "A Dude" nei commenti della risposta accettata:

Si compila, perché è necessario che funzioni in quel modo in base alle specifiche del linguaggio, ad es.le specifiche Java Lang lo dicono:

6.6.1 Determinazione dell'accessibilità (almeno da JLS6)

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

Cioè.l'"ambito di accesso" di un membro privato è:ovunque entro i confini lessicali dell’organismo di classe di livello superiore.

Questo significa:è possibile accedere a tutti i membri privati ​​definiti all'interno del corpo della classe più esterna da qualsiasi altro punto di questo corpo della classe.

Ad esempio, è possibile accedere al metodo privato di una classe interna dai metodi della classe esterna o da qualsiasi metodo di un'altra classe interna della classe esterna.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top