Por que o Java não permite a substituição de métodos estáticos?


534

Por que não é possível substituir métodos estáticos?

Se possível, use um exemplo.


3
A maioria dos idiomas OOP não permite isso.
jmucchiello

7
@ jmucchiello: veja minha resposta. Eu estava pensando o mesmo que você, mas aprendi sobre os métodos de 'classe' do Ruby / Smalltalk e, portanto, existem outras linguagens OOP verdadeiras que fazem isso.
Kevin Brock

5
@jmucchiello língua mais OOP não são linguagem real OOP (penso em Smalltalk)
mathk


1
pode ser porque o Java resolve chamadas para métodos estáticos em tempo de compilação. Portanto, mesmo se você tiver escrito Parent p = new Child()e, em seguida, p.childOverriddenStaticMethod()o compilador resolverá o problema Parent.childOverriddenStaticMethod()observando o tipo de referência.
precisa

Respostas:


494

A substituição depende de ter uma instância de uma classe. O objetivo do polimorfismo é que você pode subclassificar uma classe e os objetos que implementam essas subclasses terão comportamentos diferentes para os mesmos métodos definidos na superclasse (e substituídos nas subclasses). Um método estático não está associado a nenhuma instância de uma classe, portanto o conceito não é aplicável.

Havia duas considerações que influenciaram o design do Java. Uma era a preocupação com o desempenho: havia muitas críticas ao Smalltalk por ser muito lento (coleta de lixo e chamadas polimórficas fazendo parte disso) e os criadores de Java estavam determinados a evitar isso. Outra foi a decisão de que o público-alvo para Java eram desenvolvedores de C ++. Fazer com que os métodos estáticos funcionem da mesma maneira que eles tiveram o benefício de familiaridade para os programadores de C ++ e também foi muito rápido, porque não há necessidade de esperar até o tempo de execução para descobrir qual método chamar.


18
... mas apenas "correto" em Java. Por exemplo, o equivalente do Scala às "classes estáticas" (chamadas objects) permite sobrecarregar os métodos.

32
Objective-C também permite substituir métodos de classe.
Richard

11
Há uma hierarquia de tipos em tempo de compilação e uma hierarquia de tipos em tempo de execução. Faz todo o sentido perguntar por que uma chamada de método estático não se beneficia da hierarquia de tipos de tempo de execução nas circunstâncias em que existe uma. Em Java, isso acontece ao chamar um método estático de um objeto ( obj.staticMethod()) - que é permitido e usa os tipos de tempo de compilação. Quando a chamada estática está em um método não estático de uma classe, o objeto "atual" pode ser um tipo derivado da classe - mas os métodos estáticos definidos nos tipos derivados não são considerados (eles estão no tipo de tempo de execução hierarquia).
9788 Steve

18
Eu deveria ter deixado claro: é não verdade que o conceito não é aplicável .
9788 Steve

13
Essa resposta, embora correta, é mais parecida com "como é" do que como deveria ser ou com mais precisão como poderia ser, a fim de atender às expectativas do OP e, portanto, aqui, eu e os outros. Não há razão concreta para impedir a substituição de métodos estáticos além de "é assim que é". Eu acho que é uma falha pessoalmente.
RichieHH

186

Pessoalmente, acho que essa é uma falha no design do Java. Sim, sim, entendo que métodos não estáticos estão anexados a uma instância, enquanto métodos estáticos estão anexados a uma classe, etc. etc. Ainda assim, considere o seguinte código:

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");
    }
}

Este código não funcionará conforme o esperado. Ou seja, os funcionários especiais recebem um bônus de 2%, assim como os funcionários regulares. Mas se você remover as "estáticas", os funcionários especiais recebem um bônus de 3%.

(É certo que este exemplo é um estilo de codificação ruim, pois na vida real você provavelmente desejaria que o multiplicador de bônus estivesse em um banco de dados em algum lugar, em vez de codificado. de código irrelevante ao ponto.)

Parece-me bastante plausível que você queira tornar estático getBonusMultiplier. Talvez você queira exibir o multiplicador de bônus para todas as categorias de funcionários, sem precisar ter uma instância de funcionário em cada categoria. Qual seria o sentido de procurar exemplos de exemplos? E se estamos criando uma nova categoria de funcionário e ainda não temos nenhum funcionário atribuído a ela? Isso é logicamente uma função estática.

Mas isso não funciona.

E sim, sim, posso pensar em várias maneiras de reescrever o código acima para fazê-lo funcionar. Meu argumento não é que ele crie um problema insolúvel, mas que crie uma armadilha para o programador incauto, porque a linguagem não se comporta como eu acho que uma pessoa razoável esperaria.

Talvez se eu tentasse escrever um compilador para uma linguagem OOP, veria rapidamente por que seria difícil ou impossível implementá-lo para que as funções estáticas possam ser substituídas.

Ou talvez haja alguma boa razão pela qual o Java se comporte dessa maneira. Alguém pode apontar uma vantagem para esse comportamento, alguma categoria de problema que é facilitada por isso? Quero dizer, não me aponte apenas para a especificação da linguagem Java e diga "veja, isso está documentado como ela se comporta". Eu sei disso. Mas há uma boa razão pela qual DEVE se comportar dessa maneira? (Além do óbvio "fazê-lo funcionar direito era muito difícil" ...)

Atualizar

@ VicKirk: Se você quer dizer que esse é um "design ruim" porque não se encaixa na maneira como o Java lida com a estática, minha resposta é: "Bem, claro, é claro". Como eu disse no meu post original, ele não funciona. Mas se você quer dizer que é um projeto ruim, no sentido de que haveria algo de fundamentalmente errado com uma linguagem em que isso funcionasse, ou seja, onde a estatística pudesse ser substituída como funções virtuais, isso introduziria de alguma forma uma ambiguidade ou seria impossível implementar eficientemente ou algo assim, respondo: "Por quê? O que há de errado com o conceito?"

Eu acho que o exemplo que dou é uma coisa muito natural de se querer fazer. Eu tenho uma classe que tem uma função que não depende de nenhum dado da instância e que eu poderia razoavelmente querer chamar independente de uma instância, além de querer chamar de dentro de um método de instância. Por que isso não deve funcionar? Eu já enfrentei essa situação várias vezes ao longo dos anos. Na prática, eu contorno isso, tornando a função virtual e, em seguida, criando um método estático cujo único objetivo na vida é ser um método estático que transmite a chamada para o método virtual com uma instância fictícia. Parece uma maneira muito indireta de chegar lá.


11
@Bemrose: Mas esse é o meu ponto: por que eu não deveria fazer isso? Talvez meu conceito intuitivo do que uma "estática" deva fazer seja diferente do seu, mas basicamente penso em uma estática como um método que PODE ser estático porque não usa nenhum dado de instância e que DEVE ser estático porque você pode querer chamá-lo independentemente de uma instância. Uma estática está claramente vinculada a uma classe: espero que Integer.valueOf esteja vinculado a Inteiros e Double.valueOf esteja vinculado a Doubles.
Jay

9
@ewernli & Bemrose: Sim, sim, é assim que é. Eu não estou debatendo isso. Como o código no meu exemplo não funciona, é claro que eu não tentaria escrevê-lo. Minha pergunta é por que é assim. (Receio que isso esteja se transformando em uma daquelas conversas em que não estamos nos comunicando. "Com licença, Sr. Salesman, posso pegar uma dessas em vermelho?" "Não, custa US $ 5". "Sim, eu sei que custa US $ 5, mas posso comprar um vermelho? "" Senhor, acabei de lhe dizer que custa US $ 5 "." Ok, eu sei o preço, mas estava perguntando sobre a cor. "" Eu já disse o preço! " etc)
Jay

6
Penso que, em última análise, este código é confuso. Considere se a instância é passada como um parâmetro. Então você está dizendo que a instância de tempo de execução deve ditar qual método estático é chamado. Isso basicamente torna toda uma hierarquia separada paralela à instância existente. Agora, e se uma subclasse definir a mesma assinatura de método que não estática? Eu acho que as regras tornariam as coisas bastante complicadas. É precisamente esse tipo de complicação de linguagem que o Java tenta evitar.
Yishai

6
@Yishai: RE "A instância de tempo de execução dita qual método estático é chamado": Exatamente. Não vejo por que você não pode fazer nada com uma estática que você pode fazer com uma virtual. "Hierarquia separada": eu diria: faça parte da mesma hierarquia. Por que as estatísticas não estão incluídas na mesma hierarquia? "Subclasse define a mesma assinatura como não estática": presumo que seria ilegal, assim como é ilegal, digamos, que uma subclasse substitua uma função com uma assinatura idêntica, mas com um tipo de retorno diferente, ou para não lançar todas as exceções que o pai joga, ou tem um escopo mais restrito.
Jay

28
Acho que Jay tem razão - me surpreendeu também quando descobri que a estatística não podia ser substituída. Em parte porque se eu tenho A com o método someStatic()e B estende A, B.someMethod() vincula -o ao método em A. Se eu adicionar posteriormente someStatic()a B, o código de chamada ainda será chamado A.someStatic()até eu recompilar o código de chamada. Também me surpreendeu que bInstance.someStatic()use o tipo declarado de bInstance, não o tipo de tempo de execução, porque ele liga no compile, não no link, portanto, A bInstance; ... bInstance.someStatic()chama A.someStatic () se houver B.someStatic ().
Lawrence Dol

42

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 methodera um método não estático e ThisClass.method()se methodera 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, privatee protectedcomo qualificadores em staticmétodos de uma classe. Teríamos, então todos têm que se acostumar com o fato de que private statice public finalmé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.


"Se o fizéssemos, isso tornaria os corpos dos métodos mais difíceis de ler: chamadas estáticas em uma classe pai poderiam ser dinamicamente" redirecionadas "." É verdade, mas é exatamente isso que acontece com as chamadas de funções não estáticas comuns agora. Isso é rotineiramente apontado como um recurso positivo para funções virtuais comuns, não como um problema.
Jay

25

Na verdade, estávamos errados.
Apesar de Java não permitir que você substitua métodos estáticos por padrão, se você examinar minuciosamente a documentação das classes Class e Method em Java, ainda poderá encontrar uma maneira de emular a substituição de métodos estáticos seguindo a seguinte solução 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());
    }
}

Resultado resultante:

0.02
0.02
0.02
0.03

o que estávamos tentando alcançar :)

Mesmo se declararmos a terceira variável Carl como RegularEmployee e atribuir a ela uma instância de SpecialEmployee, ainda teremos a chamada do método RegularEmployee no primeiro caso e a chamada do método SpecialEmployee no segundo caso

RegularEmployee Carl = new SpecialEmployee();

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

basta olhar para o console de saída:

0.02
0.03

;)


9
Sim reflexão é praticamente a única coisa que se pode fazer - mas a questão não é exatamente isso - útil para tê-lo aqui, porém
Mr_and_Mrs_D

1
Esta resposta é o maior hack que eu já vi até agora em todos os tópicos de Java. Foi divertido para lê-lo ainda :)
Andrejs

19

Os métodos estáticos são tratados como globais pela JVM, não estão vinculados a uma instância de objeto.

Conceitualmente, seria possível se você pudesse chamar métodos estáticos de objetos de classe (como em linguagens como Smalltalk), mas não é o caso em Java.

EDITAR

Você pode sobrecarregar o método estático, tudo bem. Mas você não pode substituir um método estático, porque a classe não é um objeto de primeira classe. Você pode usar a reflexão para obter a classe de um objeto em tempo de execução, mas o objeto obtido não é paralelo à hierarquia de classes.

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

Você pode refletir sobre as aulas, mas isso pára por aí. Você não invoca um método estático usando clazz1.staticMethod(), mas usando MyClass.staticMethod(). Um método estático não está vinculado a um objeto e, portanto, não há noção thisnem superem um método estático. Um método estático é uma função global; como conseqüência, também não há noção de polimorfismo e, portanto, a substituição do método não faz sentido.

Mas isso poderia ser possível se MyClassfosse um objeto em tempo de execução no qual você invoca um método, como no Smalltalk (ou talvez no JRuby como um comentário sugere, mas não sei nada sobre o JRuby).

Ah sim ... mais uma coisa. Você pode invocar um método estático através de um objeto, obj1.staticMethod()mas esse açúcar realmente sintático para MyClass.staticMethod()e deve ser evitado. Geralmente, gera um aviso no IDE moderno. Não sei por que eles permitiram esse atalho.


5
Mesmo muitas linguagens modernas como Ruby têm métodos de classe e permitem substituí-los.
Chandra Sekar

3
As classes existem como objetos em Java. Veja a classe "Class". Posso dizer myObject.getClass () e ele retornará uma instância do objeto de classe apropriado.
Jay

5
Você obtém apenas uma "descrição" da classe - não a própria classe. Mas a diferença é sutil.
ewernli

Você ainda tem classe, mas está oculto na VM (perto do carregador de classes), o usuário quase não tem acesso a ela.
mathk

Para usar clazz2 instanceof clazz1corretamente, você pode usar class2.isAssignableFrom(clazz1), o que acredito que retornaria verdadeiro no seu exemplo.
Simon Forsberg

14

A substituição do método é possibilitada pelo despacho dinâmico , o que significa que o tipo declarado de um objeto não determina seu comportamento, mas seu tipo de tempo de execução:

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

Embora ambos lassiee kermitsejam declarados como objetos do tipo Animal, seu comportamento (método .speak()) varia porque o despacho dinâmico vincula apenas a chamada do método .speak()a uma implementação em tempo de execução - não em tempo de compilação.

Agora, é aqui que a staticpalavra - chave começa a fazer sentido: a palavra "estático" é um antônimo de "dinâmico". Portanto, a razão pela qual você não pode substituir os métodos estáticos é porque não há expedição dinâmica nos membros estáticos - porque estática significa literalmente "não dinâmico". Se eles fossem enviados dinamicamente (e, portanto, pudessem ser substituídos), a staticpalavra - chave não faria mais sentido.


11

Sim. Praticamente Java permite substituir o método estático, e Não, teoricamente, se você substituir um método estático em Java, ele será compilado e executado sem problemas, mas perderá o polimorfismo, que é a propriedade básica do Java. Você lerá em todos os lugares que não é possível tentar compilar e executar. você receberá sua resposta. por exemplo, se você possui a classe Animal e um método estático eat () e você substitui esse método estático em sua subclasse, vamos chamá-lo de cão. Então, quando quer que você atribua um objeto Dog a uma referência animal e chame eat () de acordo com o comando eat Java de Java, deveria ter sido chamado, mas em Substituir estático, o eat () dos animais será chamado.

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

De acordo com o princípio de polimorfismo de Java, a saída deve ser Dog Eating.
Mas o resultado foi diferente porque, para oferecer suporte ao polimorfismo, o Java usa a ligação tardia, o que significa que os métodos são chamados apenas no tempo de execução, mas não no caso de métodos estáticos. Nos métodos estáticos, o compilador chama métodos no tempo de compilação e não no tempo de execução, portanto, obtemos métodos de acordo com a referência e não de acordo com o objeto, uma referência a contendo é por isso que 't.


3
É uma prática ruim chamar métodos estáticos do objeto.
Dmitry Zagorulkin

6

a substituição é reservada para membros da instância para suportar comportamento polimórfico. membros da classe estática não pertencem a uma instância específica. em vez disso, membros estáticos pertencem à classe e, como resultado, a substituição não é suportada porque as subclasses herdam apenas membros de instância pública e protegida e não membros estáticos. Você pode definir uma fábrica de interface e / ou padrões de design de estratégia para avaliar uma abordagem alternativa.


1
Você não leu nenhuma das outras respostas já abordando isso e deixando claro que essas não são razões suficientes para desconsiderar as estáticas superiores no nível conceitual. Sabemos que não funciona. É perfeitamente "limpo desejar a substituição de métodos estáticos e, de fato, é possível em muitas outras línguas."
RichieHH

Richard, vamos supor por um minuto que, quando respondi a essa pergunta há 4 anos, a maioria dessas respostas não havia sido postada, então não seja idiota! Não há necessidade de afirmar que não li com atenção. Além disso, você não leu que estamos discutindo apenas a substituição em relação ao Java. Quem se importa com o que é possível em outros idiomas. É irrelevante. Vá trollar em outro lugar. Seu comentário não adiciona nada de valor a este tópico.
Athens Holloway

6

Em Java (e em muitas linguagens OOP, mas não posso falar por todos; e algumas não têm estática), todos os métodos têm uma assinatura fixa - os parâmetros e tipos. Em um método virtual, o primeiro parâmetro está implícito: uma referência ao próprio objeto e, quando chamado de dentro do objeto, o compilador adiciona automaticamente this.

Não há diferença para métodos estáticos - eles ainda têm uma assinatura fixa. No entanto, ao declarar o método estático, você declarou explicitamente que o compilador não deve incluir o parâmetro de objeto implícito no início dessa assinatura. Portanto, qualquer outro código que chama isso não deve tentar colocar uma referência a um objeto na pilha . Se fizesse isso, a execução do método não funcionaria, pois os parâmetros estariam no lugar errado - deslocados por um - na pilha.

Por causa dessa diferença entre os dois; Os métodos virtuais sempre têm uma referência ao objeto de contexto (ou seja this), portanto, é possível fazer referência a qualquer coisa dentro da pilha que pertença à instância do objeto. Mas com métodos estáticos, como não há referência passada, esse método não pode acessar nenhuma variável e método de objeto, pois o contexto não é conhecido.

Se você deseja que o Java altere a definição para que um contexto de objeto seja passado para todos os métodos, estáticos ou virtuais, em essência você teria apenas métodos virtuais.

Como alguém perguntou em um comentário à operação - qual é o motivo e o propósito de desejar esse recurso?

Não conheço muito o Ruby, como isso foi mencionado pelo OP, fiz algumas pesquisas. Vejo que nas classes Ruby é realmente um tipo especial de objeto e é possível criar (mesmo dinamicamente) novos métodos. Classes são objetos de classe completos em Ruby, eles não estão em Java. Isso é apenas algo que você terá que aceitar ao trabalhar com Java (ou C #). Essas não são linguagens dinâmicas, embora o C # esteja adicionando algumas formas de dinâmica. Na realidade, Ruby não possui métodos "estáticos" até onde pude encontrar - nesse caso, são métodos no objeto de classe singleton. Você pode substituir esse singleton por uma nova classe e os métodos no objeto de classe anterior chamarão aqueles definidos na nova classe (correto?). Portanto, se você chamasse um método no contexto da classe original, ele ainda executaria apenas a estática original, mas chamar um método na classe derivada chamaria métodos da matriz ou subclasse. Interessante e posso ver algum valor nisso. É preciso um padrão de pensamento diferente.

Como você está trabalhando em Java, precisará se ajustar a essa maneira de fazer as coisas. Por que eles fizeram isso? Bem, provavelmente para melhorar o desempenho na época, com base na tecnologia e no entendimento disponíveis. As linguagens de computador estão em constante evolução. Volte o suficiente e não existe OOP. No futuro, haverá outras novas idéias.

EDIT : Mais um comentário. Agora que vejo as diferenças e como desenvolvedor Java / C #, consigo entender por que as respostas que você obtém dos desenvolvedores Java podem ser confusas se você vem de uma linguagem como Ruby. Os staticmétodos Java não são os mesmos que os classmétodos Ruby . Os desenvolvedores de Java terão dificuldade em entender isso, assim como os que trabalham principalmente com uma linguagem como Ruby / Smalltalk. Eu posso ver como isso também seria muito confuso pelo fato de o Java também usar "método de classe" como outra maneira de falar sobre métodos estáticos, mas esse mesmo termo é usado de maneira diferente por Ruby. Java não possui métodos de classe de estilo Ruby (desculpe); O Ruby não possui métodos estáticos no estilo Java, que são realmente apenas funções de estilo processual antigas, como encontrado em C.

By the way - obrigado pela pergunta! Hoje aprendi algo novo para mim sobre métodos de classe (estilo Ruby).


1
Obviamente, não há razão para que o Java não possa transmitir um objeto "Class" como um parâmetro oculto para métodos estáticos. Apenas não foi projetado para fazê-lo.
jmucchiello

6

Bem ... a resposta é NÃO se você pensar da perspectiva de como um método substituído deve se comportar em Java. Mas, você não receberá nenhum erro do compilador se tentar substituir um método estático. Isso significa que, se você tentar substituir, o Java não o impedirá de fazer isso; mas você certamente não obtém o mesmo efeito dos métodos não estáticos. A substituição em Java significa simplesmente que o método específico seria chamado com base no tipo de tempo de execução do objeto e não no tipo de tempo de compilação (que é o caso dos métodos estáticos substituídos). Ok ... alguma suposição pela razão pela qual eles se comportam estranhamente? Como são métodos de classe e, portanto, o acesso a eles sempre é resolvido durante o tempo de compilação, usando apenas as informações do tipo de tempo de compilação.

Exemplo : vamos tentar ver o que acontece se tentarmos substituir um método estático: -

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();
    // ...
}
}

Saída : -
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod

Observe a segunda linha da saída. Se o staticMethod tivesse sido substituído, essa linha deveria ser idêntica à terceira linha, pois estamos chamando o 'staticMethod ()' em um objeto do Runtime Type como 'SubClass' e não como 'SuperClass'. Isso confirma que os métodos estáticos são sempre resolvidos usando apenas suas informações de tipo de tempo de compilação.


5

Em geral, não faz sentido permitir a substituição de métodos estáticos, pois não haveria uma boa maneira de determinar qual deles chamar em tempo de execução. Tomando o exemplo Employee, se chamarmos RegularEmployee.getBonusMultiplier () - qual método deve ser executado?

No caso de Java, pode-se imaginar uma definição de linguagem na qual é possível 'substituir' métodos estáticos, desde que sejam chamados por meio de uma instância de objeto. No entanto, tudo o que isso faria é reimplementar métodos de classe regulares, adicionando redundância ao idioma sem realmente adicionar nenhum benefício.


2
Intuitivamente, eu pensaria que deveria funcionar como uma função virtual. Se B estender A e ambos A e B tiverem funções virtuais denominadas doStuff, o compilador saberá que as instâncias de A devem usar A.doStuff e as instâncias de B devem usar B.doStuff. Por que não pode fazer o mesmo com funções estáticas? Afinal, o compilador sabe de que classe cada objeto é uma instância.
Jay

Erm ... Jay, um método necessidade estático não ser (geralmente não é) chamado em uma instância ...
meriton

2
@meriton, mas é ainda mais fácil, não? Se um método estático for chamado usando um nome de classe, você usaria o método apropriado para a classe.
CPerkins

Mas, então, o que está substituindo está fazendo por você. Se você chamar A.doStuff (), deve usar a versão que foi substituída em "B estende A" ou a versão que foi substituída em "C estende A". E se você tem C ou B, está chamando essas versões de qualquer maneira ... não é necessário substituir.
PSpeed

@meriton: É verdade que um método estático geralmente não é chamado com uma instância, mas presumo que seja porque essa chamada não faz nada útil, dado o design atual do Java! Estou sugerindo que um design alternativo poderia ter sido uma idéia melhor. BTW, em um sentido muito real, as funções estáticas são chamadas com uma instância com bastante frequência: Quando você chama a função estática de dentro de uma função virtual. Então você obtém implicitamente this.function (), ou seja, a instância atual.
22410 Jay Jay

5

Ao substituir, podemos criar uma natureza polimórfica, dependendo do tipo de objeto. O método estático não tem relação com o objeto. Portanto, o java não pode suportar a substituição de método estático.


5

Gosto e duplico o comentário de Jay ( https://stackoverflow.com/a/2223803/1517187 ).
Concordo que esse é o design ruim do Java.
Muitos outros idiomas suportam a substituição de métodos estáticos, como vemos nos comentários anteriores. Eu sinto que Jay também veio do Delphi para Java como eu.
Delphi (Object Pascal) foi o primeiro idioma a implementar OOP.
É óbvio que muitas pessoas tiveram experiência com esse idioma, pois no passado era o único idioma para escrever produtos comerciais de GUI. E - sim, nós poderíamos no Delphi substituir métodos estáticos. Na verdade, os métodos estáticos no Delphi são chamados de "métodos de classe", enquanto o Delphi tinha o conceito diferente de "métodos estáticos Delphi", que eram métodos com ligação inicial. Para substituir os métodos que você teve que usar ligação tardia, declare a diretiva "virtual". Por isso, foi muito conveniente e intuitivo e eu esperaria isso em Java.


3

Que bem fará para substituir os métodos estáticos. Você não pode chamar métodos estáticos por meio de uma instância.

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

EDIT: Parece que, através de uma supervisão infeliz no design de linguagem, você pode chamar métodos estáticos por meio de uma instância. Geralmente ninguém faz isso. Foi mal.


8
"Você não pode chamar métodos estáticos por meio de uma instância" Na verdade, uma das peculiaridades de Java é que você PODE chamar métodos estáticos por meio de uma instância, mesmo que seja uma idéia extremamente ruim.
Powerlord

1
Na verdade Java não permite que membros estáticos para ser acessado via casos: ver variável estática em Java
Richard JP Le Guen

Um IDE moderno e adequado gera um aviso quando você faz isso, portanto, pelo menos você pode capturá-lo enquanto o Oracle pode manter a compatibilidade com versões anteriores.
Gimby

1
Não há nada conceitualmente errado em poder chamar um método estático por meio de uma instância. Isso é brincadeira por causa da brincadeira. Por que algo como uma instância de Date NÃO chama seus próprios métodos estáticos, passando dados da instância para a função através da interface de chamada?
RichieHH

@RichieHH Não é sobre isso que eles estão discutindo. A questão é por que é permitido chamar variable.staticMethod(), em vez de Class.staticMethod(), onde variableé uma variável com o tipo declarado Class. Eu concordo que é um design de linguagem ruim.
fishinear próximo de 24/1118

3

A substituição em Java simplesmente significa que o método específico seria chamado com base no tipo de tempo de execução do objeto e não no tipo de tempo de compilação (que é o caso dos métodos estáticos substituídos). Como métodos estáticos são métodos de classe, eles não são métodos de instância; portanto, eles não têm nada a ver com o fato de que referência está apontando para qual Objeto ou instância, porque, devido à natureza do método estático, ele pertence a uma classe específica. Você pode redeclará-lo na subclasse, mas essa subclasse não saberá nada sobre os métodos estáticos da classe pai, porque, como eu disse, é específica apenas para a classe em que foi declarada. Acessá-los usando referências a objetos é apenas uma liberdade extra concedida pelos designers de Java e certamente não devemos pensar em interromper essa prática apenas quando eles a restringirem mais detalhes e exemplo http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html


3

Ao substituir, você obtém polimorfismo dinâmico. Quando você diz substituir métodos estáticos, as palavras que você está tentando usar são contraditórias.

Static diz - o tempo de compilação, a substituição é usada para o polimorfismo dinâmico. Ambos têm natureza oposta e, portanto, não podem ser usados ​​juntos.

O comportamento polimórfico dinâmico ocorre quando um programador usa um objeto e acessa um método de instância. O JRE mapeará diferentes métodos de instância de diferentes classes com base no tipo de objeto que você está usando.

Quando você diz substituir métodos estáticos, métodos estáticos que acessaremos usando o nome da classe, que será vinculado no tempo de compilação, para que não haja conceito de vincular métodos em tempo de execução com métodos estáticos. Portanto, o próprio termo "substituir" os métodos estáticos não faz sentido.

Nota: mesmo se você acessar um método de classe com um objeto, o compilador java ainda é inteligente o suficiente para descobrir e fará a vinculação estática.


Isso não é verdade em muitas instâncias em que, em tempo de execução, o método estático é de fato chamado de uma instância da classe que contém e, portanto, é perfeitamente viável determinar quais instâncias da função chamar.
RichieHH

estático não significa tempo de compilação, estático significa que está vinculado à classe e não a qualquer objeto específico. faz muito mais sentido do que criar uma fábrica de classes, exceto que a estática Box.createBoxfaz mais sentido do que BoxFactory.createBoxe é um padrão inevitável ao precisar verificar a construção de erros sem gerar exceções (os construtores não podem falhar, eles podem apenas matar o processo / lançamento exceção), enquanto um método estático pode retornar nulo em caso de falha ou até mesmo aceitar retornos de chamada de sucesso / erro para escrever algo como haste.bin/codajahati.java .
Dmitry

2

A resposta a esta pergunta é simples, o método ou a variável marcada como estática pertence apenas à classe. Portanto, o método estático não pode ser herdado na subclasse porque eles pertencem apenas à superclasse.


1
Olá G4uKu3_Gaurav. Obrigado por decidir contribuir. No entanto, geralmente esperamos respostas mais longas e detalhadas do que isso.
DJClayworth

@DJClayworth, você deve seguir este link para obter uma resposta detalhada geeksforgeeks.org/…
g1ji

Obrigado pelo link. Na verdade, estou aqui para ajudar os recém-chegados ao site, para explicar como o site funciona para aqueles que não estão acostumados, não porque eu precisava de uma resposta para a pergunta.
precisa

1

Solução fácil: use instância singleton. Isso permitirá substituições e herança.

No meu sistema, tenho a classe SingletonsRegistry, que retorna a instância para a classe passada. Se a instância não for encontrada, ela será criada.

Classe de idioma 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!";
  }

}

Muito legal, é a primeira vez que ouviu falar sobre a linguagem de programação Haxe :)
cyc115

1
Isso é muito melhor implementado em Java como um método estático na própria classe; Singleton.get(). O registro é apenas sobrecarga de clichê, e impede o GC nas classes.
Lawrence Dol

Você está certo, é uma solução clássica. Não me lembro exatamente por que escolhi o registro, provavelmente tinha alguma estrutura de pensamento que levou a esse resultado.
Raivo Fishmeister

1

Um método estático, variável, bloco ou classe aninhada pertence à classe inteira e não a um objeto.

Um método em Java é usado para expor o comportamento de um objeto / classe. Aqui, como o método é estático (isto é, o método estático é usado para representar apenas o comportamento de uma classe). Alterar / substituir o comportamento de toda a classe violará o fenômeno de um dos pilares fundamentais da programação orientada a objetos, ou seja, alta coesão . (lembre-se de que um construtor é um tipo especial de método em Java.)

Coesão Alta - Uma classe deve ter apenas um papel. Por exemplo: Uma classe de carro deve produzir apenas objetos de carro e não bicicleta, caminhões, aviões etc. Mas a classe Car pode ter alguns recursos (comportamento) que pertencem apenas a ela mesma.

Portanto, ao projetar a linguagem de programação java. Os designers de linguagem pensaram em permitir que os desenvolvedores mantivessem alguns comportamentos de uma classe apenas para si, tornando um método estático por natureza.


O código da peça abaixo tenta substituir o método estático, mas não encontrará nenhum erro de compilação.

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;
}}

Isso ocorre porque, aqui, não estamos substituindo um método, mas apenas o declarando novamente . Java permite a re-declaração de um método (estático / não estático).

A remoção da palavra-chave estática do método getVehileNumber () da classe Car resultará em erro de compilação. Como estamos tentando alterar a funcionalidade do método estático, que pertence apenas à classe Vehicle.

Além disso, se o getVehileNumber () for declarado como final , o código não será compilado, uma vez que a palavra-chave final impede o programador de declarar novamente o método.

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

No geral, isso depende de designers de software para onde usar os métodos estáticos. Pessoalmente, prefiro usar métodos estáticos para executar algumas ações sem criar nenhuma instância de uma classe. Em segundo lugar, ocultar o comportamento de uma classe do mundo exterior.


1

Aqui está uma explicação simples. Um método estático está associado a uma classe, enquanto um método de instância está associado a um objeto específico. As substituições permitem chamar a implementação diferente dos métodos substituídos associados ao objeto específico. Portanto, é contra-intuitivo substituir o método estático que nem sequer está associado aos objetos, mas à própria classe em primeiro lugar. Portanto, métodos estáticos não podem ser substituídos com base no objeto que o está chamando, ele sempre será associado à classe em que foi criado.


Como é contra-intuitivo ter uma public abstract IBox createBox();interface IBox interna? Box pode implementar o IBox para substituir o createBox e fazer com que a criação do objeto resulte em um IBox válido; caso contrário, retorne um valor nulo. Os construtores não podem retornar "nulo" e, portanto, você é forçado a (1) usar exceções EM TODA PARTE (o que fazemos agora) ou (2) criar classes de fábrica que fazem o que eu disse anteriormente, mas de uma maneira que não faz sentido para iniciantes ou especialistas de Java (o que também fazemos agora). Métodos estáticos não implementados resolvem isso.
Dmitry

-1

Agora, vendo as respostas acima, todos sabem que não podemos substituir métodos estáticos, mas não devemos entender mal o conceito de acessar métodos estáticos da subclasse .

Podemos acessar métodos estáticos da superclasse com referência à subclasse se esse método estático não tiver sido oculto pelo novo método estático definido na subclasse.

Por exemplo, veja o código abaixo: -

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");
    // }
}

Resultado:-

SuperClass saying Hello

Consulte os documentos do Java oracle e procure O que você pode fazer em uma subclasse para obter detalhes sobre como ocultar métodos estáticos na subclasse.

obrigado


-3

O código a seguir mostra que é possível:

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
Não, não faz; o tipo declarado estático de não osmé . OverridenStaticMethOverrideStaticMeth
Lawrence Dol

2
Além disso, eu tentaria evitar usar tanto metanfetamina durante a programação de <grande sorriso>;
Lawrence Dol
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.