Perché non chiamare un metodo statico tramite un'istanza un errore per il compilatore Java?

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

  •  03-07-2019
  •  | 
  •  

Domanda

Sono sicuro che tutti conoscete il comportamento intendo - codice come:

Thread thread = new Thread();
int activeCount = thread.activeCount();

provoca un avviso del compilatore. Perché non è un errore?

Modifica

Per essere chiari: la domanda non ha nulla a che fare con le discussioni. Mi rendo conto che gli esempi di discussione vengono spesso forniti quando ne discutiamo a causa della possibilità di rovinare davvero le cose con loro. Ma in realtà il problema è che tale uso è sempre senza senso e non puoi (con competenza) scrivere una tale chiamata e intenderla. Qualsiasi esempio di questo tipo di chiamata al metodo sarebbe barmy. Eccone un altro:

String hello = "hello";
String number123AsString = hello.valueOf(123);

Il che fa sembrare che ogni istanza di String sia dotata di " String valueOf (int i) " metodo.

È stato utile?

Soluzione

Fondamentalmente credo che i progettisti Java abbiano fatto un errore quando hanno progettato il linguaggio, ed è troppo tardi per risolverlo a causa dei problemi di compatibilità coinvolti. Sì, può portare a codice molto fuorviante. Sì, dovresti evitarlo. Sì, dovresti assicurarti che il tuo IDE sia configurato per trattarlo come un errore, IMO. Se dovessi mai progettare un linguaggio da solo, tienilo a mente come esempio del tipo di cosa da evitare :)

Solo per rispondere al punto di DJClayworth, ecco cosa è permesso in C #:

public class Foo
{
    public static void Bar()
    {
    }
}

public class Abc
{
    public void Test()
    {
        // Static methods in the same class and base classes
        // (and outer classes) are available, with no
        // qualification
        Def();

        // Static methods in other classes are available via
        // the class name
        Foo.Bar();

        Abc abc = new Abc();

        // This would *not* be legal. It being legal has no benefit,
        // and just allows misleading code
        // abc.Def();
    }

    public static void Def()
    {
    }
}

Perché penso che sia fuorviante? Perché se guardo il codice someVariable.SomeMethod () mi aspetto che usi il valore di someVariable . Se SomeMethod () è un metodo statico, tale aspettativa non è valida; il codice mi sta ingannando. Come può essere una buona cosa?

Stranamente, Java non ti permetterà di usare una variabile potenzialmente non inizializzata per chiamare un metodo statico, nonostante il fatto che l'unica informazione che utilizzerà sarà il tipo dichiarato della variabile. È un disordine incoerente e inutile. Perché permetterlo?

EDIT: questa modifica è una risposta alla risposta di Clayton, che afferma che consente l'ereditarietà per i metodi statici. Non I metodi statici non sono polimorfici. Ecco un programma breve ma completo per dimostrarlo:

class Base
{
    static void foo()
    {
        System.out.println("Base.foo()");
    }
}

class Derived extends Base
{
    static void foo()
    {
        System.out.println("Derived.foo()");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Base b = new Derived();
        b.foo(); // Prints "Base.foo()"
        b = null;
        b.foo(); // Still prints "Base.foo()"
    }
}

Come puoi vedere, il valore del tempo di esecuzione di b viene completamente ignorato.

Altri suggerimenti

Perché dovrebbe essere un errore? L'istanza ha accesso a tutti i metodi statici. I metodi statici non possono cambiare lo stato dell'istanza (provare a è un errore di compilazione).

Il problema con l'esempio ben noto che dai è molto specifico per thread , non per chiamate di metodo statiche. Sembra che tu stia ottenendo il activeCount () per il thread a cui fa riferimento il thread , ma stai davvero ottenendo il conteggio per il thread chiamante. Questo è un errore logico che stai facendo come programmatore. Emettere un avviso è la cosa appropriata da fare per il compilatore in questo caso. Sta a te ascoltare l'avvertimento e correggere il tuo codice.

EDIT: mi rendo conto che la sintassi della lingua è ciò che ti consente di scrivere un codice fuorviante, ma ricorda che anche il compilatore e i suoi avvertimenti fanno parte della lingua. Il linguaggio ti consente di fare qualcosa che il compilatore considera dubbioso, ma ti dà l'avvertimento per assicurarti di essere consapevole che potrebbe causare problemi.

Non possono più renderlo un errore, a causa di tutto il codice che è già disponibile.

Sono con te sul fatto che dovrebbe essere un errore. Forse dovrebbe esserci un'opzione / profilo per il compilatore per aggiornare alcuni avvisi agli errori.

Aggiornamento: quando hanno introdotto la parola chiave assert in 1.4, che presenta potenziali problemi di compatibilità simili con il vecchio codice, l'hanno resa disponibile solo se imposti esplicitamente la modalità sorgente su " 1.4 " . Suppongo che si potrebbe fare un errore in una nuova modalità sorgente "java 7". Ma dubito che lo farebbero, considerando che tutta la seccatura che causerebbe. Come altri hanno sottolineato, non è strettamente necessario impedirti di scrivere codice confuso. E le modifiche alla lingua in Java dovrebbero essere limitate allo stretto necessario a questo punto.

Risposta breve: la lingua lo consente, quindi non è un errore.

Probabilmente per la stessa logica che rende questo non un errore:

public class X
{
    public static void foo()
    {
    }

    public void bar()
    {
        foo(); // no need to do X.foo();
    }
}

La cosa veramente importante, dal punto di vista del compilatore, è che sia in grado di risolvere i simboli. Nel caso di un metodo statico, deve sapere in quale classe cercarlo, poiché non è associato ad alcun oggetto particolare. I progettisti di Java hanno ovviamente deciso che, poiché potevano determinare la classe di un oggetto, potevano anche risolvere la classe di qualsiasi metodo statico per quell'oggetto da qualsiasi istanza dell'oggetto. Hanno scelto di consentire questo - influenzato, forse, dall'osservazione di @ TofuBeer - di dare al programmatore un po 'di praticità. I progettisti di altre lingue hanno fatto diverse scelte. Probabilmente sarei caduto in quest'ultimo campo, ma non è un grosso problema per me. Probabilmente consentirei l'utilizzo menzionato da @TofuBeer, ma avendo consentito la mia posizione sul non consentire l'accesso da una variabile di istanza è meno sostenibile.

Non è un errore perché fa parte delle specifiche, ma ovviamente stai chiedendo della logica, che tutti possiamo immaginare.

La mia ipotesi è che l'origine di ciò sia in realtà consentire a un metodo in una classe di invocare un metodo statico nella stessa classe senza la seccatura. Dato che chiamare x () è legale (anche senza il nome di auto classe), anche chiamare this.x () dovrebbe essere legale, e quindi anche chiamare tramite qualsiasi oggetto è stato reso legale.

Questo aiuta anche a incoraggiare gli utenti a trasformare le funzioni private in statiche se non cambiano lo stato.

Inoltre, i compilatori generalmente cercano di evitare di dichiarare errori quando non è possibile che ciò comporti un errore diretto. Poiché un metodo statico non modifica lo stato o la cura dell'oggetto invocante, non causa un errore effettivo (solo confusione) per consentire ciò. È sufficiente un avvertimento.

Lo scopo del riferimento alla variabile di istanza è solo quello di fornire il tipo che racchiude la statica. Se si osserva il codice byte che richiama uno statico tramite instance.staticMethod o EnclosingClass.staticMethod produce lo stesso metodo statico invoke bytecode. Non viene visualizzato alcun riferimento all'istanza.

La risposta è anche perché è lì dentro, beh lo è. Finché usi la classe. e non tramite un'istanza contribuirai a evitare confusione in futuro.

Probabilmente puoi cambiarlo nel tuo IDE (in Preferenze Eclipse - > Java - > Compiler - > Errori / Avvertimenti)

Non c'è opzione per questo. In java (come molte altre lingue) puoi avere accesso a tutti i membri statici di una classe attraverso il nome della sua classe o l'oggetto istanza di quella classe. Dipenderebbe da te e dal tuo caso e dalla tua soluzione software quale dovresti usare per darti maggiore leggibilità.

Lo considero solo:

instanceVar.staticMethod();

per essere una scorciatoia per questo:

instanceVar.getClass().staticMethod();

Se hai sempre dovuto farlo:

SomeClass.staticMethod();

allora non saresti in grado di sfruttare l'ereditarietà per i metodi statici.

Cioè, chiamando il metodo statico tramite l'istanza non è necessario sapere quale classe concreta è l'istanza al momento della compilazione, solo che implementa staticMethod () da qualche parte lungo la catena di eredità.

EDIT: questa risposta è sbagliata. Vedi i commenti per i dettagli.

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