2012-05-05 12 views
10

Ho uno strano problema che non riesco a capire quando ho cercato di estrapolare il mio programma. Un ulteriore problema è che non sono in grado di creare un semplice caso di test, perché ogni volta che provo funziona. Ci deve essere qualche complicazione che mi manca. Ma cercherò di descrivere la situazione nel modo più chiaro possibile, nel caso in cui suoni familiare a chiunque.Impossibile accedere al membro protetto della superclasse dallo stesso pacchetto in un diverso contenitore

Ho una classe base chiamata Seed che fa parte dell'applicazione principale e viene caricata dal classloader di sistema. Ho un plugin che contiene una classe Road che è una sottoclasse di Seed. Viene caricato in fase di runtime da un file jar separato. La classe Road fa riferimento al campo Seed.garden, che è definito come:

giardino finale protetto;

Nota che non ottengo errori di compilazione. Inoltre, non ricevo errori di runtime quando il jar del plug-in è incluso nel classpath del sistema. Solo quando la mia applicazione principale carica il plug-in usando un nuovo classloader (che ha il programma di caricamento di sistema come genitore) ottengo l'errore. L'errore è:

java.lang.IllegalAccessError: tentato di accedere campo package.Seed.garden dalla classe package.Road $ 4

Deve avere qualcosa a che fare con il fatto che la sottoclasse è stato caricato da un caricatore di classe diverso rispetto alla superclasse, ma non riesco a trovare alcun motivo ufficiale per cui ciò non dovrebbe funzionare. Inoltre, come ho detto, quando provo a riprodurre il problema con un semplice caso di test (che include i vasi separati, caricando la sottoclasse con un diverso programma di caricamento classi, ecc.), Non ottengo l'errore.

Inoltre, non sembra probabile che io stia violando le regole di accesso poiché funziona quando le classi vengono caricate dallo stesso classloader e non ottengo errori di compilazione.

Sono fuori di idee! Qualcuno riconosce questo problema, o ha qualche indicazione per me per le indicazioni su cui guardare? Aiuto!

+0

Dovrei aggiungere che Road $ 4 è una classe interna anonima di Road (ovviamente), ma ho incluso questo fatto nel mio caso di test, e ha funzionato ancora. –

+1

possibile duplicato di [Sovrascrivere il metodo di accesso predefinito tra diversi classloader rompe il polimorfismo] (http://stackoverflow.com/questions/4060842/overriding-default-accessor-method-across-different-classloaders-breaks-polymorp) – axtavt

+0

@axtavt: potresti avere ragione, sebbene quell'articolo riguardi l'accesso predefinito, non l'accesso protetto. Suppongo che lo stesso valga per l'accesso protetto. Ho dato un'occhiata alle specifiche, ma non ho capito la cosa del "pacchetto di runtime". Non spiega perché il mio caso di test funziona. Vedrò se riesco a farlo rompere usando questa conoscenza. Grazie! –

risposta

6

OK, quindi con l'aiuto di axtavt e altri rispondenti ho capito qual è il problema. Le altre risposte hanno aiutato, ma non hanno capito esattamente, motivo per cui sto rispondendo alla mia stessa domanda. Il problema si è rivelato essere il concetto di "pacchetti runtime", definita nel Java Virtual Machine specification come segue:

5.3 Creazione e caricamento

... In fase di esecuzione, una classe o interfaccia non è determinato solo dal suo nome, ma da una coppia: il suo nome completo e il caricatore della classe che definisce. Ogni classe o interfaccia appartiene a un singolo pacchetto runtime. Il pacchetto di runtime di una classe o di un'interfaccia è determinato dal nome del pacchetto e dal caricatore di classe che definisce la classe o l'interfaccia. ...

5.4.4 Access Control

... Un campo o il metodo R è accessibile a una classe o interfaccia D se e solo se una delle seguenti condizioni: ..

  • R è protetto ed è dichiarato in una classe C, e D è una sottoclasse di C o C stesso.
  • R è sia protetto o un pacchetto privato (vale a dire, né pubblico né protetta né privato), ed è dichiarata da una classe nello stesso pacchetto di runtime di D.

La prima clausola spiega perché strada è consentito per accedere a Seed.garden, poiché Road è una sottoclasse di Seed e la seconda clausola spiega perché a Road $ 4 non è consentito accedervi, nonostante si trovi nello stesso pacchetto di Road, dal momento che non si trova nello stesso pacchetto di runtime , essendo stato caricato da un caricatore di classi diverse. La restrizione non è in realtà una restrizione della lingua Java, è una restrizione della VM Java.

Quindi la conclusione per la mia situazione è che l'eccezione si verifica a causa di una legittima restrizione della Java VM, e ho intenzione di aggirarla, probabilmente rendendo pubblici i campi, che non è un problema in questo caso poiché sono definitivi, e non segreti, o forse esportando Seed.garden a Road $ 4 tramite Road, che ha accesso.

Grazie a tutti per i vostri suggerimenti e risposte!

+0

Grazie molto utile. Una cosa da aggiungere - non è necessario rendere pubblici quei campi protetti, basta inserire un metodo nella sottocategoria (e può essere privato) che restituisce quei valori che sono protetti. È solo la classe anonima che non è in grado di accedere ai campi protetti e non alla sottoclasse. –

+0

@MichaelWiles grazie. Questo era in realtà ciò che intendevo con il mio secondo suggerimento. :) –

4

Sembra che tu abbia una crisi di identità di classe, con due caricatori di classi diversi che caricano la stessa classe nella gerarchia di classi o simili. Leggi alcuni sui caricatori di classe java. Ecco una buona introduzione, per "crisi dell'identità di classe" vedere la figura 2: http://www.ibm.com/developerworks/java/library/j-dyn0429/

+0

Non è una crisi di identità di classe (ogni classe è caricata solo da un classloader), ma credo che la causa sia simile, come sottolineato nella domanda axtavt collegata a sopra, vale a dire il fatto che le classi sono in "pacchetti runtime" diversi ". –

+0

Non sono sicuro di quale sia la differenza, a che cosa si riferisce il "pacchetto di runtime", ci si riferisce ai diversi spazi dei nomi introdotti dai classloader? Quindi stiamo parlando della stessa cosa. –

+0

Sì. "Pacchetto runtime" è ciò che la specifica Java Virtual Machine chiama la combinazione di un caricatore di pacchetti e classi. Ho pensato che per "crisi dell'identità di classe" intendevi il caricamento della classe _same_ due volte usando diversi caricatori di classe. –

0

Questo è un errore di autorizzazione, quindi dipende dal framework che si utilizza per eseguire il runtime. Giusto per chiarire questo è davvero questo, rendere pubblico il membro genitore, e quindi provare a eseguire. Nel caso in cui tutto sia a posto, ripristina il codice e, in base al runtime utilizzato, è necessario configurare l'accesso di sicurezza corretto.

+0

Non è un semplice errore di autorizzazione, altrimenti non sarebbe nemmeno compilato. Ma penso che l'abbiamo trovato, vedere i miei commenti alla domanda originale. –

+0

tramite autorizzazione - Voglio dire autorizzazione di runtime, non tempo statico/di compilazione. Ad ogni modo sembra che tu abbia trovato il vero problema. – miks

1

I should add that Road$4 is an anonymous inner class of Road...

Qualcun altro pensato che questo era un bug così nel 1998:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4116802

An inner class has no greater access to members of another class than a top-level class, except for those members that are declared within an enclosing, enclosed, or sibling class. It is a common misconception that an inner class has unrestricted access to inherited members of its enclosing classes. This is not true.

avrei probabilmente la ricerca che fatto un po 'di più, però, perché questo è stato segnalato in origine contro Java 1.2 , ma mi sembra di ricordare dalla mia lettura che questo è vero anche oggi.

EDIT:

ho confermato che questo è vero:

http://docs.oracle.com/javase/tutorial/java/javaOO/summarynested.html

La possibilità di una classe interna anonima è solo il punto in cui è definito. Quindi non avrà accesso ai membri ereditati, anche se la classe esterna lo fa.

+0

Informazioni interessanti. Penso che non si applica però, perché protetto implica l'accesso al pacchetto, e la classe interna si trova nello stesso pacchetto (nel mio caso) come la superclasse della sua classe che lo racchiude, quindi dovrebbe avere accesso ai membri protetti della superclasse. Destra? Altrimenti non dovrebbe nemmeno compilare, cosa che fa. –

Problemi correlati