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 method
fosse un metodo non statico e ThisClass.method()
se method
fosse 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
, private
e protected
come qualificazioni su static
metodi di una classe. Ci sarebbe poi tutti dobbiamo abituarci al fatto che private static
e public final
metodi non vengono sovrascritte, e possono quindi essere risolto in modo sicuro al momento della compilazione, e sono "sicuri" per leggere come riferimenti locali.