stranezza del compilatore java: campo dichiarato nella stessa classe, ma & # 8220; non visibile & # 8221;

StackOverflow https://stackoverflow.com/questions/1604147

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 ...

È stato utile?

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 Test s tramite 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).

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