A resposta curta é: é inteiramente possível, mas o Java não faz isso.
Aqui está um código que ilustra o estado atual das coisas em Java:
Arquivo 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();
}
}
Arquivo 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 você executar isso (eu fiz em um Mac, no Eclipse, usando Java 1.6), você obtém:
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.
Aqui, os únicos casos que podem ser uma surpresa (e de que trata a questão) parecem ser o primeiro caso:
"O tipo de tempo de execução não é usado para determinar quais métodos estáticos são chamados, mesmo quando chamados com uma instância de objeto ( obj.staticMethod()
)."
e o último caso:
"Ao chamar um método estático de dentro de um método de objeto de uma classe, o método estático escolhido é aquele acessível pela própria classe e não pela classe que define o tipo de objeto em tempo de execução."
Chamando com uma instância de objeto
A chamada estática é resolvida em tempo de compilação, enquanto uma chamada de método não estático é resolvida em tempo de execução. Observe que, embora os métodos estáticos sejam herdados (do pai), eles não são substituídos (pelo filho). Isso poderia ser uma surpresa se você esperasse o contrário.
Chamando de dentro de um método de objeto
As chamadas de método de objeto são resolvidas usando o tipo de tempo de execução, mas as chamadas de método estático ( classe ) são resolvidas usando o tipo de tempo de compilação (declarado).
Mudando as regras
Para alterar essas regras, para que a última chamada no exemplo chamado Child.printValue()
, as chamadas estáticas precisem ser fornecidas com um tipo em tempo de execução, em vez de o compilador resolver a chamada em tempo de compilação com a classe declarada do objeto (ou contexto). As chamadas estáticas podem então usar a hierarquia de tipos (dinâmicos) para resolver a chamada, assim como as chamadas de método de objeto fazem hoje.
Isso seria facilmente possível (se alterássemos o Java: -O) e não é de todo irracional, no entanto, ele tem algumas considerações interessantes.
A principal consideração é que precisamos decidir quais chamadas de método estático devem fazer isso.
No momento, Java possui essa "peculiaridade" na linguagem em que as obj.staticMethod()
chamadas são substituídas por ObjectClass.staticMethod()
chamadas (normalmente com um aviso). [ Nota: ObjectClass
é o tipo de tempo de compilação obj
.] Esses seriam bons candidatos para substituir dessa maneira, assumindo o tipo de tempo de execução de obj
.
Se o fizéssemos, isso tornaria os corpos dos métodos mais difíceis de ler: chamadas estáticas em uma classe pai poderiam potencialmente ser "redirecionadas" dinamicamente . Para evitar isso, teríamos que chamar o método estático com um nome de classe - e isso torna as chamadas mais obviamente resolvidas com a hierarquia de tipos em tempo de compilação (como agora).
As outras maneiras de chamar um método estático são mais complicadas: this.staticMethod()
devem significar o mesmo que obj.staticMethod()
, usando o tipo de tempo de execução de this
. No entanto, isso pode causar algumas dores de cabeça nos programas existentes, que chamam métodos estáticos (aparentemente locais) sem decoração (o que é possivelmente equivalente a this.method()
).
Então, e as chamadas sem adornos staticMethod()
? Sugiro que façam o mesmo de hoje e usem o contexto de classe local para decidir o que fazer. Caso contrário, haveria grande confusão. É claro que significa que method()
isso significaria this.method()
se method
era um método não estático e ThisClass.method()
se method
era um método estático. Essa é outra fonte de confusão.
Outras considerações
Se mudou este comportamento (e fez chamadas estáticas potencialmente dinamicamente não-local), nós provavelmente quer revisitar o significado de final
, private
e protected
como qualificadores em static
métodos de uma classe. Teríamos, então todos têm que se acostumar com o fato de que private static
e public final
métodos não foram substituídas, e, portanto, pode ser resolvido de forma segura em tempo de compilação, e são "seguros" para ler como referências locais.