2009-07-06 20 views
10

Si consideri il seguente caso:Java classe interna visibilità di puzzle

public class A { 
    public A() { b = new B(); } 
    B b; 
    private class B { } 
} 

Da un avvertimento in Eclipse che cito: il compilatore java emula l'a.b constructor() con un metodo di accesso sintetico. Suppongo che il compilatore vada avanti e crei un costruttore aggiuntivo "under water" per B.

Mi sembra piuttosto strano: perché la classe B non dovrebbe essere visibile come a.k.o. campo in A? E: significa che la classe B non è più privata in fase di esecuzione? E: perché si comporta in modo diverso la parola chiave protetta per la classe B?

public class A { 
    public A() { b = new B(); } 
    B b; 
    protected class B { } 
} 

risposta

24

Le classi interne sono essenzialmente un hack introdotto in Java 1.1. La JVM in realtà non ha alcun concetto di classe interiore, quindi il compilatore deve schivarlo. Il compilatore genera la classe B "al di fuori" della classe A, ma nello stesso pacchetto, quindi aggiunge accessors/costruttori sintetici per consentire ad A di accedervi.

Quando si fornisce a B un costruttore protetto, A può accedere a tale costruttore poiché si trova nello stesso pacchetto, senza che sia necessario aggiungere un costruttore sintetico.

+0

OK, lo vedo. Per me questo significa che eviterò l'uso di classi interne come nell'esempio, può solo portare a confusione. – Gerard

+2

Non vorrei lasciarti disturbare. Questo particolare avvertimento del compilatore non è molto utile a nessuno, i metodi sintetici sono sempre usati con le classi interne e non hanno alcun impatto significativo. – skaffman

+0

IMHO i metodi sintetici sono stati un'aggiunta superflua alla lingua. Solo l'utilizzo dell'ambito del pacchetto per i membri "privati" (che comunque il compilatore fa sotto il cofano) era una soluzione soddisfacente. – finnw

-1

è necessario utilizzare

this.new B(); 
+0

scusate, ma questo.nuovo B(); dà lo stesso avvertimento e comportamento. – Gerard

+2

@Rats: questo non farà differenza per il problema in mano. La "questa" qualifica è implicita. – skaffman

2

L'accesso di class B e il suo costruttore non devono essere uguali. Puoi avere una classe interna privata con un costruttore di pacchetti-scope, e questo è quello che faccio di solito.

public class A { 
    public A() { b = new B(); } 
    B b; 
    private class B { 
    B() { } 
    } 
} 
+0

Ma come si può avere un'istanza di pacchetto privato di una classe propriamente privata? – einpoklum

4

So che questa domanda è ormai quasi tre anni, ma trovo che una parte della questione non è ancora risolta:

E: significa che la classe B non è più privato in fase di esecuzione?

Carlos Heubergers commento sulla skaffmans risposta suggerisce, classe B è ancora private per le altre classi nel pacchetto.

Probabilmente ha ragione per il linguaggio di programmazione Java, vale a dire che non è possibile fare riferimento alla classe B da un'altra classe. Almeno non senza usare la riflessione (con la quale anche i membri della classe privata possono accedere dall'esterno), ma questo è un altro problema.

Ma, come la JVM non ha alcun concetto di una classe interna (come Stati skaffman), mi sono chiesto come un "accessibile da una sola classe di" visibilità si realizza a livello di bytecode. La risposta: non è affatto realizzata, per la JVM la classe interna appare come una normale classe privata del pacchetto. Questo è, se scrivi bytecode per te (o ne modifica uno generato dal compilatore) puoi accedere alla classe B senza problemi.

È possibile accedere a tutti i metodi di accesso sintetici da tutte le classi nello stesso pacchetto. Pertanto, se si assegna un valore a un campo privato della classe A in un metodo della classe B, un metodo di accesso sintetico con visibilità predefinita (cioè privato del pacchetto) viene generato nella classe A (denominata qualcosa come access$000) che imposta il valore per l'utente. Questo metodo dovrebbe essere chiamato solo dalla classe B (e infatti può essere chiamato solo da lì usando il linguaggio Java).Ma dal punto di vista della JVM, questo è solo un metodo come qualsiasi altro e può essere chiamato da qualsiasi classe.

Quindi, per rispondere alla domanda:

  • Dal Java lingue punto di vista, di classe B è e rimane privata.
  • Dal punto di vista della JVM, la classe B (o meglio: classe A$B) non è privata.
+0

corretto, ma non è quello che ho suggerito. Ho scritto "i ** membri ** sono ancora privati" - intendevo i campi della classe e non la ** classe ** come interpretata da te! Anche questa domanda riguarda [tag: java] e non [tag: bytecode] (generazione, modifica, _hacking_ ...). –

+0

@CarlosHeuberger ovviamente questa non era un'offesa per te! Certamente i membri della classe B sono ancora privati, ma la classe B (come una sorta di membro della classe A) non lo è (dal punto di vista della JVM), e questa era la domanda originale di Gerard. E sì, la domanda riguarda java, ma come affermato nel tag wiki: "Java è un linguaggio di programmazione e ** ambiente di runtime **". E l'ambiente di runtime è la JVM e la JVM di per sé non ha nulla a che fare con il linguaggio di programmazione Java, ma interpreta solo bytecode. – siegi