stranezza del compilatore java: campo dichiarato nella stessa classe, ma & # 8220; non visibile & # 8221;
-
05-07-2019 - |
Domanda
Il compilatore eclipse rifiuta di compilare il seguente codice, affermando che il campo s non è visibile. (Anche il compilatore Aspect J di IBM rifiuta, affermando che "non è stato possibile risolvere") Perché?
public class Test {
String s;
void foo(Object o) {
String os = getClass().cast(o).s;
}
}
Le specifiche della lingua Java indicano:
Altrimenti, diciamo che c'è un valore predefinito accesso, che è consentito solo quando l'accesso avviene dall'interno pacchetto in cui viene dichiarato il tipo.
Per come lo capisco, il campo è dichiarato e accessibile nella stessa unità di compilazione, quindi all'interno dello stesso pacchetto, e quindi dovrebbe essere accessibile.
Ancora più stranamente, aggiungendo un downcast da ? estende Test
su Test
rende visibile il campo, ovvero la seguente compilazione di codice:
public class Test {
String s;
void foo(Object o) {
Test t = getClass().cast(o);
String os = t.s;
}
}
Mi sono imbattuto in un bug del compilatore o ho frainteso le specifiche Java?
Modifica Sono su un altro computer ora. Qui, javac accetta il codice, ma eclipse ancora no. Versioni su questa macchina:
Piattaforma Eclipse
Versione: 3.4.2 ID build: M20090211-1700
JDK 1.6.0
Modifica 2 In effetti, javac accetta il codice. Avevo testato eseguendo la build ant, che utilizza il compilatore Ascpect J di IBM ...
Soluzione
Prova questo:
void foo(Object o) {
Test foo = getClass().cast(o);
String so = foo.s;
}
[Modifica per chiarire]:
getClass (). cast (o)
restituisce un oggetto di tipo ' capture # 1-of? estende Test
'e non Test
. Quindi il problema è legato ai generici e al modo in cui il compilatore lo tratta. Non conosco i dettagli delle specifiche sui generici ma dato che alcuni compilatori (per i commenti qui) accettano il tuo codice, allora questo è un buco nella specifica o alcuni di questi compilatori non sono del tutto conformi alle specifiche.
[Ultimi pensieri]:
Credo che il compilatore di eclissi sia effettivamente (con attenzione) corretto qui. L'oggetto o
può in effetti essere un'estensione di Test (e definito in un altro pacchetto) e il compilatore non ha modo di sapere se è effettivamente così o no. Quindi lo tratta come il caso peggiore di un'istanza di un'estensione definita in un altro pacchetto. Sarebbe stato super corretto se l'aggiunta di un final
alla classe Test
avrebbe consentito l'accesso al campo s
, ma non lo è.
Altri suggerimenti
Bene, vediamo. Direi che il compilatore non può garantire correttamente che foo ()
sarà chiamato da qualche entità all'interno del pacchetto, e quindi non può garantire che s
sia visibile. Ad esempio, aggiungi
protected void bar() {
foo();
}
e poi in una sottoclasse Banana
in un altro pacchetto
public void quux() { bar(); }
e oops! getClass ()
produce Banana
, che non può vedere s
.
Modifica: in un certo senso, other.package.Banana non ha un campo s
. Se Banana fosse nello stesso pacchetto, potrebbe comunque avere la sua proprietà s
e dovrebbe fare riferimento al di
tramite Test
s super
.
Non riesco a riprodurre ciò che stai dicendo. Entrambi compilano bene per me senza preavviso, errore o altro direttamente con javac.
WinXP, javac 1.6.0_16
No, ho provato con eclipse (v3.4.1, ID build: M20080911-1700) e per la prima dice:
The field Test.s is not visible
Almeno per livello di conformità del compilatore 1.6 e 1.5.
La cosa divertente è che, se guardi le opzioni di correzione rapida, elenca una risoluzione Cambia in 's'
. Il che ovviamente non risolve il problema. Quindi il compilatore di eclissi e il generatore di "riparazione rapida" sembra avere opinioni diverse anche su questo ;-)
Per il livello di conformità del compilatore 1.4 (come era prevedibile) in eclissi per il primo che ottengo
s cannot be resolved or is not a field
e per il secondo ottengo
Type mismatch: cannot convert from Object to Test
Se specifico -source 1.4
e target -1.4
nella riga di comando direttamente javac
dice per il primo
cannot find symbol
e per il secondo ottengo
incompatible types
In realtà in quasi tutti i casi, tranne quando richiesto da Generics, è meglio (e più sicuro) usare l'operatore di cast Java. Ne ho discusso qui . L'operatore di cast Java ha uno sguardo dettagliato, ma qui è lo strumento giusto.
Sostituendo il metodo cast
con l'operatore si compila bene in Eclipse.
public class Test {
String s;
void foo(Object o) {
String os = ((Test) o).s;
}
}
Penso che alphazero sia corretto qui , che Eclipse è troppo cauto.
Molto strano. Per una ragione sconosciuta (per me), il compilatore eclipse richiede un cast esplicito:
void foo(Object o) {
String os = ((Test)getClass().cast(o)).s;
}
Mentre il codice si compila perfettamente senza il cast con Sun JDK (sto eseguendo la versione 1.6.0_16 su GNU / Linux).