Perché Java non consente l'override di metodi statici?


534

Perché non è possibile ignorare i metodi statici?

Se possibile, si prega di utilizzare un esempio.


3
La maggior parte delle lingue OOP non lo consente.
jmucchiello,

7
@jmucchiello: vedi la mia risposta. Stavo pensando lo stesso di te, ma poi ho imparato i metodi di 'classe' di Ruby / Smalltalk e quindi ci sono altri veri linguaggi OOP che lo fanno.
Kevin Brock,

5
@jmucchiello la maggior parte delle lingue OOP non sono vere lingue OOP (penso a Smalltalk)
matematica


1
potrebbe essere perché Java risolve le chiamate a metodi statici in fase di compilazione. Quindi, anche se hai scritto Parent p = new Child()e p.childOverriddenStaticMethod()il compilatore lo risolverà Parent.childOverriddenStaticMethod()esaminando il tipo di riferimento.
Manoj,

Risposte:


494

L'override dipende dall'avere un'istanza di una classe. Il punto del polimorfismo è che puoi sottoclassare una classe e gli oggetti che implementano quelle sottoclassi avranno comportamenti diversi per gli stessi metodi definiti nella superclasse (e sovrascritti nelle sottoclassi). Un metodo statico non è associato a nessuna istanza di una classe, quindi il concetto non è applicabile.

Ci sono state due considerazioni alla base del design di Java che hanno avuto un impatto su questo. Uno riguardava le prestazioni: c'erano state molte critiche a Smalltalk sul fatto che fosse troppo lento (la raccolta dei rifiuti e le chiamate polimorfiche facessero parte di questo) e i creatori di Java erano determinati a evitarlo. Un'altra è stata la decisione che il pubblico di destinazione per Java fosse sviluppatori C ++. Far funzionare i metodi statici nel modo in cui hanno avuto il vantaggio della familiarità per i programmatori C ++ ed è stato anche molto veloce, perché non è necessario attendere fino al runtime per capire quale metodo chiamare.


18
... ma solo "corretto" in Java. Ad esempio, l'equivalente di Scala delle "classi statiche" (che vengono chiamate objects) consente il sovraccarico dei metodi.

32
Objective-C consente inoltre di sostituire i metodi di classe.
Richard

11
Esiste una gerarchia di tipi in fase di compilazione e una gerarchia di tipi in fase di runtime. Ha perfettamente senso chiedersi perché una chiamata a metodo statico non si avvale della gerarchia dei tipi di runtime in quelle circostanze in cui ce n'è una. In Java ciò accade quando si chiama un metodo statico da un oggetto ( obj.staticMethod()), che è consentito e utilizza i tipi di tempo di compilazione. Quando la chiamata statica si trova in un metodo non statico di una classe, l'oggetto "corrente" potrebbe essere un tipo derivato della classe, ma i metodi statici definiti sui tipi derivati ​​non vengono considerati (sono nel tipo di runtime gerarchia).
Steve Powell,

18
Avrei messo in chiaro: è non è vero che il concetto non è applicabile .
Steve Powell,

13
Questa risposta, sebbene corretta, è più simile a "com'è" piuttosto che a come dovrebbe essere o più accuratamente come potrebbe essere al fine di soddisfare le aspettative del PO e, quindi, di me e degli altri. Non vi è alcun motivo concreto per impedire l'override di metodi statici diversi da "così è". Penso che sia un difetto personalmente.
RichieHH,

186

Personalmente penso che questo sia un difetto nella progettazione di Java. Sì, sì, capisco che i metodi non statici sono collegati a un'istanza mentre i metodi statici sono collegati a una classe, ecc. Ecc. Tuttavia, si consideri il seguente codice:

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(getBonusMultiplier());
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

Questo codice non funzionerà come ci si potrebbe aspettare. Vale a dire, SpecialEmployee ottiene un bonus del 2% proprio come i normali dipendenti. Ma se rimuovi i "statici", allora SpecialEmployee ottiene un bonus del 3%.

(Certo, questo esempio è uno stile di codifica scadente in quanto nella vita reale probabilmente vorresti che il moltiplicatore di bonus fosse in un database da qualche parte piuttosto che in un codice hard. Ma è solo perché non volevo impantanare l'esempio con molto di codice irrilevante al punto.)

Mi sembra abbastanza plausibile che tu possa voler rendere statico getBonusMultiplier. Forse vuoi essere in grado di visualizzare il moltiplicatore di bonus per tutte le categorie di dipendenti, senza la necessità di avere un'istanza di un dipendente in ciascuna categoria. Quale sarebbe lo scopo di cercare esempi di questo tipo? Cosa succede se stiamo creando una nuova categoria di dipendenti e non ci sono ancora assegnati dipendenti? Questa è logicamente una funzione statica.

Ma non funziona

E sì, sì, posso pensare a un numero qualsiasi di modi per riscrivere il codice sopra per farlo funzionare. Il mio punto non è che crea un problema irrisolvibile, ma che crea una trappola per il programmatore inconsapevole, perché la lingua non si comporta come penso che una persona ragionevole si aspetterebbe.

Forse se provassi a scrivere un compilatore per un linguaggio OOP, vedrei rapidamente perché implementarlo in modo che le funzioni statiche possano essere annullate sarebbe difficile o impossibile.

O forse c'è qualche buona ragione per cui Java si comporta in questo modo. Qualcuno può segnalare un vantaggio a questo comportamento, una categoria di problemi che è resa più semplice da questo? Voglio dire, non limitarmi a specificare le specifiche del linguaggio Java e dire "vedi, questo è documentato come si comporta". Lo so. Ma c'è una buona ragione per cui DOVREBBE comportarsi in questo modo? (Oltre all'ovvio "farlo funzionare nel modo giusto era troppo difficile" ...)

Aggiornare

@VicKirk: Se intendi che si tratta di "cattiva progettazione" perché non si adatta al modo in cui Java gestisce la statica, la mia risposta è: "Beh, certo, certo." Come ho detto nel mio post originale, non funziona. Ma se intendi che è una cattiva progettazione nel senso che ci sarebbe qualcosa di fondamentalmente sbagliato in un linguaggio in cui funzionava, cioè dove la statica poteva essere ignorata proprio come le funzioni virtuali, che ciò avrebbe in qualche modo introdotto un'ambiguità o sarebbe impossibile attuare in modo efficiente o in parte, rispondo "Perché? Cosa c'è che non va nel concetto?"

Penso che l'esempio che do sia una cosa molto naturale da voler fare. Ho una classe che ha una funzione che non dipende da alcun dato di istanza e che potrei voler ragionevolmente chiamare indipendente da un'istanza, oltre a voler chiamare dall'interno di un metodo di istanza. Perché questo non dovrebbe funzionare? Ho incontrato questa situazione un buon numero di volte nel corso degli anni. In pratica ci riesco rendendo virtuale la funzione e quindi creando un metodo statico il cui unico scopo nella vita è quello di essere un metodo statico che passa la chiamata al metodo virtuale con un'istanza fittizia. Sembra un modo molto circolare per arrivarci.


11
@Bemrose: Ma questo è il mio punto: perché non dovrei essere autorizzato a farlo? Forse il mio concetto intuitivo di cosa dovrebbe fare uno "statico" è diverso dal tuo, ma fondamentalmente penso a uno statico come un metodo che può essere statico perché non utilizza alcun dato di istanza e quale DOVREBBE essere statico perché potresti volere chiamarlo indipendentemente da un'istanza. Uno statico è chiaramente legato a una classe: mi aspetto che Integer.valueOf sia legato a Integer e Double.valueOf sia legato a Doubles.
Jay,

9
@ewernli & Bemrose: Sì, sì, è così. Non lo sto discutendo. Dato che il codice nel mio esempio non funziona, ovviamente non proverei a scriverlo. La mia domanda è: perché è così. (Temo che questo si stia trasformando in una di quelle conversazioni in cui non stiamo semplicemente comunicando. "Mi scusi signor commesso, posso avere una di queste in rosso?" "No, costa $ 5." "Sì, lo so che costa $ 5, ma posso ottenerne uno rosso? "" Signore, ti ho appena detto che costa $ 5. "" Okay, conosco il prezzo, ma ti chiedevo del colore. "" Ti ho già detto il prezzo! " ecc.)
Jay

6
Penso che alla fine questo codice sia confuso. Considera se l'istanza viene passata come parametro. Quindi stai dicendo che l'istanza di runtime dovrebbe dettare quale metodo statico viene chiamato. Ciò fondamentalmente rende l'intera gerarchia separata parallela a quella dell'istanza esistente. Ora cosa succede se una sottoclasse definisce la stessa firma del metodo come non statica? Penso che le regole renderebbero le cose piuttosto complicate. Sono proprio questi tipi di complicazioni del linguaggio che Java cerca di evitare.
Yishai,

6
@Yishai: RE "L'istanza di runtime determina quale metodo statico viene chiamato": Esatto. Non vedo perché non puoi fare nulla con uno statico che puoi fare con un virtuale. "Separa gerarchia": direi, rendila parte della stessa gerarchia. Perché la statica non è inclusa nella stessa gerarchia? "La sottoclasse definisce la stessa firma non statica": presumo che sarebbe illegale, proprio come è illegale, diciamo, avere una sottoclasse sovrascrivere una funzione con una firma identica ma un tipo di ritorno diverso, o non gettare tutte le eccezioni che il genitore lancia, o per avere un ambito più ristretto.
Jay,

28
Penso che Jay abbia ragione: mi ha sorpreso anche quando ho scoperto che la statica non poteva essere ignorata. In parte perché se ho A con metodo someStatic()e B estende A, quindi si B.someMethod() lega al metodo in A. Se successivamente aggiungo someStatic()a B, il codice chiamante invoca ancora A.someStatic()fino a quando non ricompilo il codice chiamante. Inoltre mi ha sorpreso che bInstance.someStatic()utilizza il tipo dichiarato di bInstance, non il tipo di runtime perché si lega alla compilazione non al collegamento, quindi A bInstance; ... bInstance.someStatic()invoca A.someStatic () se esiste B.someStatic ().
Lawrence Dol,

42

La risposta breve è: è del tutto possibile, ma Java non lo fa.

Ecco un codice che illustra lo stato attuale delle cose in Java:

File Base.java:

package sp.trial;
public class Base {
  static void printValue() {
    System.out.println("  Called static Base method.");
  }
  void nonStatPrintValue() {
    System.out.println("  Called non-static Base method.");
  }
  void nonLocalIndirectStatMethod() {
    System.out.println("  Non-static calls overridden(?) static:");
    System.out.print("  ");
    this.printValue();
  }
}

File Child.java:

package sp.trial;
public class Child extends Base {
  static void printValue() {
    System.out.println("  Called static Child method.");
  }
  void nonStatPrintValue() {
    System.out.println("  Called non-static Child method.");
  }
  void localIndirectStatMethod() {
    System.out.println("  Non-static calls own static:");
    System.out.print("  ");
    printValue();
  }
  public static void main(String[] args) {
    System.out.println("Object: static type Base; runtime type Child:");
    Base base = new Child();
    base.printValue();
    base.nonStatPrintValue();
    System.out.println("Object: static type Child; runtime type Child:");
    Child child = new Child();
    child.printValue();
    child.nonStatPrintValue();
    System.out.println("Class: Child static call:");
    Child.printValue();
    System.out.println("Class: Base static call:");
    Base.printValue();
    System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
    child.localIndirectStatMethod();
    System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
    child.nonLocalIndirectStatMethod();
  }
}

Se esegui questo (l'ho fatto su un Mac, da Eclipse, usando Java 1.6) otterrai:

Object: static type Base; runtime type Child.
  Called static Base method.
  Called non-static Child method.
Object: static type Child; runtime type Child.
  Called static Child method.
  Called non-static Child method.
Class: Child static call.
  Called static Child method.
Class: Base static call.
  Called static Base method.
Object: static/runtime type Child -- call static from non-static method of Child.
  Non-static calls own static.
    Called static Child method.
Object: static/runtime type Child -- call static from non-static method of Base.
  Non-static calls overridden(?) static.
    Called static Base method.

Qui, gli unici casi che potrebbero essere una sorpresa (e di cui si tratta la domanda) sembrano essere il primo caso:

"Il tipo di runtime non viene utilizzato per determinare quali metodi statici vengono chiamati, anche quando viene chiamato con un'istanza di oggetto ( obj.staticMethod())."

e l' ultimo caso:

"Quando si chiama un metodo statico dall'interno di un metodo oggetto di una classe, il metodo statico scelto è quello accessibile dalla classe stessa e non dalla classe che definisce il tipo di runtime dell'oggetto."

Chiamata con un'istanza di oggetto

La chiamata statica viene risolta in fase di compilazione, mentre una chiamata al metodo non statica viene risolta in fase di esecuzione. Si noti che sebbene i metodi statici siano ereditati (dal genitore) non vengono sovrascritti (dal figlio). Questa potrebbe essere una sorpresa se ti aspettavi diversamente.

Chiamata dall'interno di un metodo oggetto

Le chiamate al metodo oggetto vengono risolte utilizzando il tipo di runtime, ma le chiamate al metodo statico ( classe ) vengono risolte utilizzando il tipo di tempo di compilazione (dichiarato).

Cambiare le regole

Per modificare queste regole, in modo che l'ultima chiamata nell'esempio chiamato Child.printValue(), le chiamate statiche dovrebbero essere fornite con un tipo in fase di esecuzione, piuttosto che il compilatore che risolve la chiamata in fase di compilazione con la classe dichiarata dell'oggetto (o contesto). Le chiamate statiche potrebbero quindi utilizzare la gerarchia del tipo (dinamico) per risolvere la chiamata, proprio come fanno oggi le chiamate al metodo degli oggetti.

Questo sarebbe facilmente fattibile (se cambiassimo Java: -O), e non è affatto irragionevole, tuttavia ha alcune considerazioni interessanti.

La considerazione principale è che dobbiamo decidere quali chiamate a metodi statici dovrebbero fare questo.

Al momento, Java ha questa "stranezza" nella lingua in cui le obj.staticMethod()chiamate sono sostituite da ObjectClass.staticMethod()chiamate (normalmente con un avviso). [ Nota: ObjectClass è il tipo di tempo di compilazione di obj.] Questi sarebbero buoni candidati per l'override in questo modo, prendendo il tipo di runtime di obj.

Se lo facessimo renderebbe più difficile la lettura dei corpi dei metodi: le chiamate statiche in una classe genitore potrebbero potenzialmente essere "reindirizzate" dinamicamente . Per evitare ciò, dovremmo chiamare il metodo statico con un nome di classe e questo rende le chiamate più ovviamente risolte con la gerarchia dei tipi di compilazione (come ora).

Gli altri modi per invocare un metodo statico sono più complicati: this.staticMethod()dovrebbero significare lo stesso obj.staticMethod(), prendendo il tipo di runtime di this. Tuttavia, ciò potrebbe causare alcuni mal di testa con programmi esistenti, che chiamano metodi statici (apparentemente locali) senza decorazione (che è probabilmente equivalente a this.method()).

E le chiamate disadorno staticMethod()? Suggerisco di fare lo stesso di oggi e usano il contesto di classe locale per decidere cosa fare. Altrimenti ne conseguirebbe una grande confusione. Ovviamente significa che method()significherebbe this.method()se methodfosse un metodo non statico e ThisClass.method()se methodfosse un metodo statico. Questa è un'altra fonte di confusione.

Altre considerazioni

Se abbiamo cambiato questo comportamento (e fatto le chiamate statiche potenzialmente dinamico non-locale), ci sarebbe probabilmente vuole rivisitare il significato di final, privatee protectedcome qualificazioni su staticmetodi di una classe. Ci sarebbe poi tutti dobbiamo abituarci al fatto che private statice public finalmetodi non vengono sovrascritte, e possono quindi essere risolto in modo sicuro al momento della compilazione, e sono "sicuri" per leggere come riferimenti locali.


"Se lo facessimo renderebbe più difficile la lettura dei corpi dei metodi: le chiamate statiche in una classe genitore potrebbero potenzialmente essere" reindirizzate "dinamicamente." Vero, ma questo è esattamente ciò che accade ora con le normali chiamate di funzione non statica. Questo è regolarmente pubblicizzato come una caratteristica positiva per le normali funzioni virtuali, non un problema.
Jay,

25

In realtà ci sbagliavamo.
Nonostante Java non ti permetta di sovrascrivere i metodi statici di default, se guardi attentamente la documentazione delle classi Class e Method in Java, puoi ancora trovare un modo per emulare i metodi statici sostituendo la seguente soluzione alternativa:

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;

class RegularEmployee {

    private BigDecimal salary = BigDecimal.ONE;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }
    public BigDecimal calculateBonus() {
        return salary.multiply(this.getBonusMultiplier());
    }
    public BigDecimal calculateOverridenBonus() {
        try {
            // System.out.println(this.getClass().getDeclaredMethod(
            // "getBonusMultiplier").toString());
            try {
                return salary.multiply((BigDecimal) this.getClass()
                    .getDeclaredMethod("getBonusMultiplier").invoke(this));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
    // ... presumably lots of other code ...
}

final class SpecialEmployee extends RegularEmployee {

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

public class StaticTestCoolMain {

    static public void main(String[] args) {
        RegularEmployee Alan = new RegularEmployee();
        System.out.println(Alan.calculateBonus());
        System.out.println(Alan.calculateOverridenBonus());
        SpecialEmployee Bob = new SpecialEmployee();
        System.out.println(Bob.calculateBonus());
        System.out.println(Bob.calculateOverridenBonus());
    }
}

Risultato risultante:

0.02
0.02
0.02
0.03

cosa stavamo cercando di ottenere :)

Anche se dichiariamo la terza variabile Carl come RegularEmployee e assegniamo a questa istanza di SpecialEmployee, avremo comunque la chiamata del metodo RegularEmployee nel primo caso e la chiamata del metodo SpecialEmployee nel secondo caso

RegularEmployee Carl = new SpecialEmployee();

System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());

basta guardare la console di output:

0.02
0.03

;)


9
Sì, la riflessione è praticamente l'unica cosa che si può fare - ma la domanda non è esattamente questa - utile per averla qui però
Mr_and_Mrs_D

1
Questa risposta è l'hack più grande che abbia mai visto finora su tutti gli argomenti Java. È stato divertente leggerlo ancora :)
Andrejs

19

I metodi statici sono trattati come globali dalla JVM, non sono affatto associati a un'istanza di oggetto.

Concettualmente potrebbe essere possibile se si potessero chiamare metodi statici da oggetti di classe (come in linguaggi come Smalltalk) ma non è il caso di Java.

MODIFICARE

Puoi sovraccaricare il metodo statico, va bene. Ma non è possibile sovrascrivere un metodo statico, poiché le classi non sono oggetti di prima classe. È possibile utilizzare reflection per ottenere la classe di un oggetto in fase di esecuzione, ma l'oggetto che si ottiene non è parallelo alla gerarchia di classi.

class MyClass { ... }
class MySubClass extends MyClass { ... }

MyClass obj1 = new MyClass();
MySubClass obj2 = new MySubClass();

ob2 instanceof MyClass --> true

Class clazz1 = obj1.getClass();
Class clazz2 = obj2.getClass();

clazz2 instanceof clazz1 --> false

Puoi riflettere sulle lezioni, ma si ferma qui. Non invocare un metodo statico utilizzando clazz1.staticMethod(), ma utilizzando MyClass.staticMethod(). Un metodo statico non è associato a un oggetto e quindi non esiste alcuna nozione thissuperin un metodo statico. Un metodo statico è una funzione globale; di conseguenza non vi è nemmeno alcuna nozione di polimorfismo e, pertanto, la sostituzione del metodo non ha senso.

Ma questo potrebbe essere possibile se MyClassfosse un oggetto in fase di esecuzione su cui invochi un metodo, come in Smalltalk (o forse JRuby come suggerisce un commento, ma non so nulla di JRuby).

Ah già, un'altra cosa. Puoi invocare un metodo statico attraverso un oggetto, obj1.staticMethod()ma quello zucchero davvero sintattico per MyClass.staticMethod()e dovrebbe essere evitato. Solitamente genera un avviso nell'IDE moderno. Non so perché abbiano mai permesso questa scorciatoia.


5
Anche molte lingue moderne come Ruby hanno metodi di classe e consentono di ignorarle.
Chandra Sekar,

3
Le classi esistono come oggetti in Java. Vedi la classe "Class". Posso dire myObject.getClass () e mi restituirà un'istanza dell'oggetto di classe appropriato.
Jay,

5
Ottieni solo una "descrizione" della classe, non la classe stessa. Ma la differenza è sottile.
ewernli,

Hai ancora classe ma è nascosta nella VM (vicino al programma di caricamento classi), l'utente non ha quasi accesso ad essa.
mathk,

Per usare clazz2 instanceof clazz1correttamente puoi invece usare class2.isAssignableFrom(clazz1), che credo tornerebbe vero nel tuo esempio.
Simon Forsberg,

14

La sostituzione del metodo è resa possibile dal dispacciamento dinamico , il che significa che il tipo dichiarato di un oggetto non determina il suo comportamento, ma piuttosto il suo tipo di runtime:

Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"

Anche se entrambi lassiee kermitsono dichiarati come oggetti di tipo Animal, il loro comportamento (metodo .speak()) varia perché il dispacciamento dinamico vincolerà solo la chiamata del metodo .speak()a un'implementazione in fase di esecuzione, non in fase di compilazione.

Ora, ecco dove la staticparola chiave inizia a dare un senso: la parola "statico" è un inno per "dinamico". Pertanto, il motivo per cui non è possibile ignorare i metodi statici è perché non vi è alcun dispacciamento dinamico sui membri statici, poiché statico significa letteralmente "non dinamico". Se spedissero dinamicamente (e quindi potessero essere sostituiti) la staticparola chiave non avrebbe più senso.


11

Sì. Praticamente Java consente di ignorare il metodo statico e No teoricamente se si sostituisce un metodo statico in Java, verrà compilato ed eseguito senza problemi, ma perderà il polimorfismo che è la proprietà di base di Java. Leggerai ovunque che non è possibile provare a compilare ed eseguire. otterrai la tua risposta. ad esempio, se si dispone di animali di classe e di un metodo statico eat () e si ignora quel metodo statico nella sua sottoclasse, lo si chiama Dog. Quindi quando dovresti assegnare un oggetto Dog a un riferimento animale e chiamare eat () secondo Java Dog's dovrebbe essere stato chiamato, ma in statico Overriding Animals 'eat () verrà chiamato.

class Animal {
    public static void eat() {
        System.out.println("Animal Eating");
    }
}

class Dog extends Animal{
    public static void eat() {
        System.out.println("Dog Eating");
    }
}

class Test {
    public static void main(String args[]) {
       Animal obj= new Dog();//Dog object in animal
       obj.eat(); //should call dog's eat but it didn't
    }
}


Output Animal Eating

Secondo il principio del polimorfismo di Java, l'output dovrebbe essere Dog Eating.
Ma il risultato è stato diverso perché per supportare il polimorfismo Java utilizza Late Binding, il che significa che i metodi vengono chiamati solo in fase di esecuzione ma non nel caso dei metodi statici. Nei metodi statici il compilatore chiama i metodi al momento della compilazione anziché in fase di esecuzione, quindi otteniamo metodi in base al riferimento e non in base all'oggetto un riferimento a che contiene il motivo per cui puoi dire che praticamente supporta il overlay statico ma teoricamente non 't.


3
È una cattiva pratica chiamare metodi statici dall'oggetto.
Dmitry Zagorulkin,

6

l'override è riservato ai membri di esempio per supportare il comportamento polimorfico. i membri della classe statica non appartengono a un'istanza particolare. invece, i membri statici appartengono alla classe e, di conseguenza, la sostituzione non è supportata perché le sottoclassi ereditano solo i membri protetti e di istanza pubblica e non i membri statici. È possibile che si desideri definire una fabbrica inerface e di ricerca e / o modelli di progettazione strategica per valutare un approccio alternativo.


1
Non hai letto nessuna delle altre risposte che già trattano questo e chiarendo che questi non sono motivi sufficienti per scartare la statica prevalente a livello concettuale. Sappiamo che non funziona. È perfettamente "pulito desiderare l'override dei metodi statici e infatti È possibile in molte altre lingue.
RichieHH,

Richard, supponiamo per un minuto che quando ho risposto a questa domanda 4 anni fa la maggior parte di queste risposte non fosse stata pubblicata, quindi non fare il coglione! Non è necessario affermare che non ho letto attentamente. Inoltre, non hai letto che stiamo solo discutendo di ignorare rispetto a Java. Chi se ne frega di ciò che è possibile in altre lingue. È irrilevante. Vai a troll altrove. Il tuo commento non aggiunge alcun valore a questa discussione.
Atene Holloway,

6

In Java (e in molti linguaggi OOP, ma non posso parlare per tutti; e alcuni non hanno affatto statico) tutti i metodi hanno una firma fissa: parametri e tipi. In un metodo virtuale, il primo parametro è implicito: un riferimento all'oggetto stesso e quando viene chiamato dall'interno dell'oggetto, il compilatore aggiunge automaticamente this.

Non c'è differenza per i metodi statici: hanno ancora una firma fissa. Tuttavia, dichiarando il metodo statico hai dichiarato esplicitamente che il compilatore non deve includere il parametro dell'oggetto implicito all'inizio di quella firma. Pertanto, qualsiasi altro codice che lo chiama non deve tentare di inserire un riferimento a un oggetto nello stack . Se lo facesse, l'esecuzione del metodo non funzionerebbe poiché i parametri sarebbero nella posizione sbagliata - spostati di uno - nello stack.

A causa di questa differenza tra i due; i metodi virtuali hanno sempre un riferimento all'oggetto contesto (cioè this), quindi è possibile fare riferimento a qualsiasi cosa nell'heap che appartiene a quell'istanza dell'oggetto. Ma con i metodi statici, poiché non viene passato alcun riferimento, tale metodo non può accedere ad alcuna variabile oggetto e metodo poiché il contesto non è noto.

Se desideri che Java cambi la definizione in modo che venga passato un contesto di oggetto per ogni metodo, statico o virtuale, in sostanza avresti solo metodi virtuali.

Come qualcuno ha chiesto in un commento all'op - qual è la tua ragione e lo scopo per volere questa funzione?

Non conosco molto Ruby, poiché questo è stato menzionato dall'OP, ho fatto alcune ricerche. Vedo che nelle classi di Ruby c'è davvero un tipo speciale di oggetto e si possono creare (anche dinamicamente) nuovi metodi. Le classi sono oggetti di classe completa in Ruby, non in Java. Questo è solo qualcosa che dovrai accettare quando lavori con Java (o C #). Questi non sono linguaggi dinamici, sebbene C # stia aggiungendo alcune forme di dinamico. In realtà, Ruby non ha metodi "statici" per quanto ho potuto trovare - in questo caso si tratta di metodi sull'oggetto classe singleton. È quindi possibile sovrascrivere questo singleton con una nuova classe e i metodi nell'oggetto classe precedente chiameranno quelli definiti nella nuova classe (corretto?). Quindi, se hai chiamato un metodo nel contesto della classe originale, eseguirà comunque solo la statica originale, ma chiamare un metodo nella classe derivata, chiamerebbe metodi dalla classe genitore o dalla sottoclasse. Interessante e posso vedere un valore in questo. Ci vuole un diverso modello di pensiero.

Dato che stai lavorando in Java, dovrai adattarti a quel modo di fare le cose. Perché l'hanno fatto? Bene, probabilmente per migliorare le prestazioni al momento in base alla tecnologia e alla comprensione disponibili. I linguaggi informatici sono in continua evoluzione. Torna abbastanza lontano e non esiste qualcosa come OOP. In futuro ci saranno altre nuove idee.

EDIT : un altro commento. Ora che vedo le differenze e mentre io stesso sviluppatore Java / C #, posso capire perché le risposte che ricevi dagli sviluppatori Java possono essere fonte di confusione se provieni da un linguaggio come Ruby. I staticmetodi Java non sono gli stessi dei classmetodi Ruby . Gli sviluppatori Java avranno difficoltà a comprenderlo, al contrario di quelli che lavorano principalmente con un linguaggio come Ruby / Smalltalk. Vedo che questo sarebbe anche molto confuso dal fatto che Java usa anche "metodo di classe" come un altro modo di parlare di metodi statici, ma lo stesso termine è usato in modo diverso da Ruby. Java non ha metodi di classe in stile Ruby (scusate); Ruby non ha metodi statici in stile Java che sono in realtà solo vecchie funzioni di stile procedurale, come si trova in C.

A proposito, grazie per la domanda! Oggi ho imparato qualcosa di nuovo sui metodi di classe (stile Ruby).


1
Naturalmente, non c'è motivo per cui Java non possa passare un oggetto "Class" come parametro nascosto a metodi statici. Semplicemente non è stato progettato per farlo.
jmucchiello,

6

Bene ... la risposta è NO se si pensa dal punto di vista di come dovrebbe comportarsi un metodo overriden in Java. Tuttavia, non si ottiene alcun errore del compilatore se si tenta di sostituire un metodo statico. Ciò significa che se si tenta di eseguire l'override, Java non ti impedisce di farlo; ma certamente non si ottiene lo stesso effetto che si ottiene per i metodi non statici. L'override in Java significa semplicemente che il particolare metodo verrebbe chiamato in base al tipo di tempo di esecuzione dell'oggetto e non al tipo di tempo di compilazione di esso (come nel caso dei metodi statici di sostituzione). Okay ... qualche supposizione per il motivo per cui si comportano in modo strano? Poiché sono metodi di classe e quindi l'accesso ad essi viene sempre risolto durante il tempo di compilazione utilizzando solo le informazioni sul tipo di tempo di compilazione.

Esempio : proviamo a vedere cosa succede se proviamo a sostituire un metodo statico: -

class SuperClass {
// ......
public static void staticMethod() {
    System.out.println("SuperClass: inside staticMethod");
}
// ......
}

public class SubClass extends SuperClass {
// ......
// overriding the static method
public static void staticMethod() {
    System.out.println("SubClass: inside staticMethod");
}

// ......
public static void main(String[] args) {
    // ......
    SuperClass superClassWithSuperCons = new SuperClass();
    SuperClass superClassWithSubCons = new SubClass();
    SubClass subClassWithSubCons = new SubClass();

    superClassWithSuperCons.staticMethod();
    superClassWithSubCons.staticMethod();
    subClassWithSubCons.staticMethod();
    // ...
}
}

Uscita : -
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod

Si noti la seconda riga dell'output. Se il staticMethod fosse stato ignorato, questa riga avrebbe dovuto essere identica alla terza riga, poiché stiamo invocando 'staticMethod ()' su un oggetto di tipo Runtime come 'SubClass' e non come 'SuperClass'. Ciò conferma che i metodi statici vengono sempre risolti utilizzando solo le informazioni sul tipo di tempo di compilazione.


5

In generale non ha senso consentire la "sostituzione" di metodi statici in quanto non ci sarebbe un buon modo per determinare quale chiamare in fase di esecuzione. Prendendo l'esempio Employee, se chiamiamo RegularEmployee.getBonusMultiplier () - quale metodo dovrebbe essere eseguito?

Nel caso di Java, si potrebbe immaginare una definizione del linguaggio in cui è possibile "sovrascrivere" i metodi statici purché vengano chiamati attraverso un'istanza di oggetto. Tuttavia, tutto ciò che farebbe sarebbe reimplementare metodi di classe regolari, aggiungendo ridondanza al linguaggio senza realmente aggiungere alcun vantaggio.


2
Intuitivamente, penso che dovrebbe funzionare proprio come una funzione virtuale. Se B estende A e sia A che B hanno funzioni virtuali denominate doStuff, il compilatore sa che le istanze di A dovrebbero usare A.doStuff e le istanze di B dovrebbero usare B.doStuff. Perché non può fare lo stesso con le funzioni statiche? Dopotutto, il compilatore conosce la classe di cui ogni oggetto è un'istanza.
Jay,

Ehm ... Jay, un metodo statico non ha bisogno di essere (generalmente non lo è) chiamato su un'istanza ...
meriton

2
@meriton, ma poi è ancora più facile, no? Se un metodo statico viene chiamato utilizzando un nome classe, si utilizzerà il metodo appropriato per la classe.
CPerkins,

Ma allora cosa sta facendo per te. Se si chiama A.doStuff (), utilizzare la versione che è stata sovrascritta in "B extends A" o la versione che è stata sovrascritta in "C extends A". E se hai C o B, stai comunque chiamando quelle versioni ... non è necessario eseguire l'override.
PSpeed,

@meriton: È vero che in genere un metodo statico non viene chiamato con un'istanza, ma presumo che ciò sia dovuto al fatto che una tale chiamata non fa nulla di utile dato l'attuale progetto di Java! Sto suggerendo che un design alternativo avrebbe potuto essere un'idea migliore. A proposito, in un senso molto reale, le funzioni statiche vengono chiamate con un'istanza piuttosto di routine: quando si chiama la funzione statica dall'interno di una funzione virtuale. Quindi ottieni implicitamente this.function (), cioè l'istanza corrente.
Jay,

5

Sovrascrivendo possiamo creare una natura polimorfica a seconda del tipo di oggetto. Il metodo statico non ha alcuna relazione con l'oggetto. Quindi java non può supportare l'override del metodo statico.


5

Mi piace e raddoppio il commento di Jay ( https://stackoverflow.com/a/2223803/1517187 ).
Sono d'accordo che questo è il cattivo design di Java.
Molte altre lingue supportano l'override dei metodi statici, come vediamo nei commenti precedenti. Sento che anche Jay è venuto a Java da Delphi come me.
Delphi (Object Pascal) è stato il primo linguaggio a implementare OOP.
È ovvio che molte persone hanno avuto esperienza con quella lingua poiché in passato era l'unica lingua a scrivere prodotti con GUI commerciali. E - sì, potremmo in Delphi ignorare i metodi statici. In realtà, i metodi statici in Delphi sono chiamati "metodi di classe", mentre Delphi aveva il diverso concetto di "metodi statici di Delphi" che erano metodi con associazione anticipata. Per sovrascrivere i metodi che è stato necessario utilizzare l'associazione tardiva, dichiarare la direttiva "virtuale". Quindi è stato molto comodo e intuitivo e me lo sarei aspettato in Java.


3

A che serve fare per sostituire i metodi statici. Non è possibile chiamare metodi statici tramite un'istanza.

MyClass.static1()
MySubClass.static1()   // If you overrode, you have to call it through MySubClass anyway.

EDIT: Sembra che attraverso una sventura svista nella progettazione del linguaggio, è possibile chiamare metodi statici attraverso un'istanza. Generalmente nessuno lo fa. Colpa mia.


8
"Non è possibile chiamare metodi statici tramite un'istanza" In realtà, una delle stranezze di Java è che PUOI chiamare metodi statici tramite un'istanza, anche se è un'idea estremamente negativa.
Powerlord

1
In effetti Java consente l'accesso ai membri statici tramite istanze: vedi Variabile statica in Java
Richard JP Le Guen,

Un IDE moderno e appropriato genererà un avviso quando lo fai, quindi almeno puoi prenderlo mentre Oracle può continuare la compatibilità con le versioni precedenti.
Gimby,

1
Non c'è nulla di concettualmente sbagliato nel poter chiamare un metodo statico attraverso un'istanza. Questo è cavillo per amor di cavillo. Perché qualcosa come un'istanza Date NON deve chiamare i propri metodi statici passando i dati dell'istanza alla funzione attraverso l'interfaccia chiamante?
RichieHH,

@RichieHH Non è per questo che stanno cavillando. La domanda è: perché è consentito chiamare variable.staticMethod(), invece di Class.staticMethod(), dove si variabletrova una variabile con il tipo dichiarato Class. Sono d'accordo che è un cattivo linguaggio di design.
fishinear del

3

L'override in Java significa semplicemente che il particolare metodo verrebbe chiamato in base al tipo di runtime dell'oggetto e non al tipo di tempo di compilazione (come nel caso dei metodi statici sovrascritti). Poiché i metodi statici sono metodi di classe, non sono metodi di istanza, quindi non hanno nulla a che fare con il fatto che il riferimento punta a quale oggetto o istanza, poiché a causa della natura del metodo statico appartiene a una classe specifica. Puoi dichiararlo nuovamente nella sottoclasse ma quella sottoclasse non saprà nulla dei metodi statici della classe genitrice perché, come ho detto, è specifico solo per quella classe in cui è stata dichiarata. Accedervi usando riferimenti a oggetti è solo una libertà in più data dai progettisti di Java e non dovremmo certamente pensare di interrompere quella pratica solo quando la limitano più dettagli ed esempi http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html


3

Sovrascrivendo, si ottiene un polimorfismo dinamico. Quando dici metodi statici prioritari, le parole che stai cercando di usare sono contraddittorie.

Dice statico: il tempo di compilazione, l'override viene utilizzato per il polimorfismo dinamico. Entrambi sono opposti in natura e quindi non possono essere usati insieme.

Il comportamento polimorfico dinamico si verifica quando un programmatore utilizza un oggetto e accede a un metodo di istanza. JRE mapperà diversi metodi di istanza di classi diverse in base al tipo di oggetto che si sta utilizzando.

Quando dici di ignorare i metodi statici, accederai ai metodi statici usando il nome della classe, che sarà collegato in fase di compilazione, quindi non esiste alcun concetto di collegamento dei metodi in fase di esecuzione con metodi statici. Quindi il termine "scavalcamento" dei metodi statici in sé non ha alcun significato.

Nota: anche se si accede a un metodo di classe con un oggetto, il compilatore java è ancora abbastanza intelligente da scoprirlo e farà collegamenti statici.


Ciò non è vero in molti casi in cui in runtime il metodo statico viene in effetti chiamato da un'istanza della classe contenitore ed è quindi perfettamente possibile determinare quali istanze della funzione richiamare.
RichieHH,

statico non significa tempo di compilazione, statico significa che è associato alla classe piuttosto che a qualsiasi oggetto specifico. ha molto se non più senso della creazione di una factory di classe, tranne statica Box.createBoxha più senso di BoxFactory.createBox, ed è un modello inevitabile quando è necessario controllare l'errore di costruzione senza generare eccezioni (i costruttori non possono fallire, possono solo uccidere il processo / lancio eccezione), mentre un metodo statico può restituire null in caso di errore o persino accettare callback di successo / errore per scrivere qualcosa come hastebin.com/codajahati.java .
Dmitry,

2

La risposta a questa domanda è semplice, il metodo o la variabile contrassegnati come statici appartengono solo alla classe, quindi quel metodo statico non può essere ereditato nella sottoclasse perché appartengono solo alla superclasse.


1
Ciao G4uKu3_Gaurav. Grazie per aver deciso di contribuire. Tuttavia, generalmente prevediamo risposte più lunghe e più dettagliate di così.
DJClayworth,

@DJClayworth dovresti seguire questo link per una risposta dettagliata geeksforgeeks.org/…
g1ji

Grazie per il link In realtà sono qui per essere utile ai nuovi arrivati ​​sul sito, per spiegare come funziona il sito per coloro che non sono abituati, non perché avevo bisogno di una risposta alla domanda.
DJClayworth,

1

Soluzione semplice: utilizzare l'istanza singleton. Consentirà l'override e l'ereditarietà.

Nel mio sistema, ho la classe SingletonsRegistry, che restituisce istanza per la classe passata. Se l'istanza non viene trovata, viene creata.

Corso di lingua Haxe:

package rflib.common.utils;
import haxe.ds.ObjectMap;



class SingletonsRegistry
{
  public static var instances:Map<Class<Dynamic>, Dynamic>;

  static function __init__()
  {
    StaticsInitializer.addCallback(SingletonsRegistry, function()
    {
      instances = null;
    });

  } 

  public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
  {
    if (instances == null) {
      instances = untyped new ObjectMap<Dynamic, Dynamic>();      
    }

    if (!instances.exists(cls)) 
    {
      if (args == null) args = [];
      instances.set(cls, Type.createInstance(cls, args));
    }

    return instances.get(cls);
  }


  public static function validate(inst:Dynamic, cls:Class<Dynamic>)
  {
    if (instances == null) return;

    var inst2 = instances[cls];
    if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
  }

}

Molto bello, è la prima volta che ho sentito parlare del linguaggio di programmazione Haxe :)
cyc115,

1
Questo è molto meglio implementato in Java come metodo statico sulla classe stessa; Singleton.get(). Il registro è solo un sovraccarico di caldaia e preclude GC sulle classi.
Lawrence Dol,

Hai ragione, è una soluzione classica. Non ricordo esattamente perché ho scelto il registro, probabilmente avevo un quadro di pensiero che ha portato a questo risultato.
Raivo Fishmeister,

1

Un metodo statico, una variabile, un blocco o una classe nidificata appartiene all'intera classe anziché a un oggetto.

Un metodo in Java viene utilizzato per esporre il comportamento di un oggetto / classe. Qui, poiché il metodo è statico (ovvero, il metodo statico viene utilizzato per rappresentare solo il comportamento di una classe), la modifica / esclusione del comportamento dell'intera classe violerà il fenomeno di uno dei pilastri fondamentali della programmazione orientata agli oggetti, ovvero l' alta coesione . (ricorda che un costruttore è un tipo speciale di metodo in Java.)

Alta coesione - Una classe dovrebbe avere un solo ruolo. Ad esempio: una classe di auto dovrebbe produrre solo oggetti di auto e non bici, camion, aerei ecc. Ma la classe di auto potrebbe avere alcune caratteristiche (comportamento) che appartengono solo a se stessa.

Pertanto, durante la progettazione del linguaggio di programmazione Java. I progettisti del linguaggio hanno pensato di consentire agli sviluppatori di mantenere alcuni comportamenti di una classe per sé solo rendendo un metodo di natura statica.


Il codice pezzo seguente tenta di sovrascrivere il metodo statico, ma non riscontrerà alcun errore di compilazione.

public class Vehicle {
static int VIN;

public static int getVehileNumber() {
    return VIN;
}}

class Car extends Vehicle {
static int carNumber;

public static int getVehileNumber() {
    return carNumber;
}}

Questo perché, qui non stiamo scavalcando un metodo ma lo stiamo solo dichiarando nuovamente. Java consente la ri-dichiarazione di un metodo (statico / non statico).

La rimozione della parola chiave statica dal metodo getVehileNumber () della classe Car comporterà un errore di compilazione, poiché stiamo provando a modificare la funzionalità del metodo statico che appartiene solo alla classe Vehicle.

Inoltre, se getVehileNumber () viene dichiarato come finale, il codice non verrà compilato, poiché la parola chiave finale impedisce al programmatore di ripetere la dichiarazione del metodo.

public static final int getVehileNumber() {
return VIN;     }

Nel complesso, ciò spetta ai progettisti di software per dove utilizzare i metodi statici. Personalmente preferisco usare metodi statici per eseguire alcune azioni senza creare alcuna istanza di una classe. In secondo luogo, per nascondere il comportamento di una classe al di fuori del mondo.


1

Ecco una semplice spiegazione. Un metodo statico è associato a una classe mentre un metodo di istanza è associato a un oggetto particolare. Le sostituzioni consentono di chiamare la diversa implementazione dei metodi sostituiti associati al particolare oggetto. Quindi è controintuitivo sovrascrivere il metodo statico che non è nemmeno associato agli oggetti ma alla classe stessa in primo luogo. Quindi i metodi statici non possono essere sovrascritti in base a quale oggetto lo chiama, sarà sempre associato alla classe in cui è stato creato.


Come è intuitivo avere un'interfaccia public abstract IBox createBox();IBox interna? Box può implementare IBox per sovrascrivere createBox e fare in modo che l'oggetto crei un IBox valido, altrimenti restituisce un valore nullo. I costruttori non possono restituire "null" e quindi sei costretto a (1) usare le eccezioni OVUNQUE (cosa facciamo ora), o (2) creare classi di fabbrica che fanno ciò che ho detto prima ma in un modo che non ha senso per i neofiti né gli esperti di Java (cosa che facciamo anche adesso). I metodi statici non implementati risolvono questo problema.
Dmitry,

-1

Ora vedendo le risposte sopra, tutti sanno che non possiamo ignorare i metodi statici, ma non si deve fraintendere il concetto di accesso ai metodi statici dalla sottoclasse .

Possiamo accedere ai metodi statici della superclasse con riferimento alla sottoclasse se questo metodo statico non è stato nascosto dal nuovo metodo statico definito nella sottoclasse.

Per esempio, vedi sotto il codice: -

public class StaticMethodsHiding {
    public static void main(String[] args) {
        SubClass.hello();
    }
}


class SuperClass {
    static void hello(){
        System.out.println("SuperClass saying Hello");
    }
}


class SubClass extends SuperClass {
    // static void hello() {
    // System.out.println("SubClass Hello");
    // }
}

Produzione:-

SuperClass saying Hello

Consulta i documenti Oracle Oracle e cerca Cosa puoi fare in una sottoclasse per i dettagli su come nascondere i metodi statici nella sottoclasse.

Grazie


-3

Il codice seguente mostra che è possibile:

class OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriden Meth");   
}   

}   

public class OverrideStaticMeth extends OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriding Meth");   
}   

public static void main(String[] args) {   
OverridenStaticMeth osm = new OverrideStaticMeth();   
osm.printValue();   

System.out.println("now, from main");
printValue();

}   

} 

1
No non lo fa; il tipo dichiarato statico di non lo osmè . OverridenStaticMethOverrideStaticMeth
Lawrence Dol,

2
Inoltre, proverei ad evitare di usare così tanto Meth durante la programmazione di <big grin>;
Lawrence Dol,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.