Comportamento do método estático final

123

Eu tenho brincado com modificadores com método estático e me deparei com um comportamento estranho.

Como sabemos, métodos estáticos não podem ser substituídos, pois estão associados à classe e não à instância.

Então, se eu tenho o trecho abaixo, ele compila bem

//Snippet 1 - Compiles fine
public class A {
    static void ts() {
    }
}

class B extends A {
    static void ts() {
    }
}

Mas se eu incluir o modificador final do método estático na classe A, a compilação falhará ts () em B e não poderá substituir ts () em A; O método substituído é final estático .

Por que isso está acontecendo quando o método estático não pode ser substituído?

Harish
fonte
2
parece estranho, +1 para a pergunta, mas até agora nenhuma das respostas é satisfatória.
Rakesh Juyal 16/11/2009
1
Não é substituído. Ainda está lá em A.ts ().
Alex Feinman

Respostas:

166

Métodos estáticos não podem ser substituídos, mas podem ser ocultados. O ts()método de B não substitui (não está sujeito a polimorfismo) o ts()de A, mas o oculta. Se você chamar ts()B (NOT A.ts()ou B.ts()... just ts()), o de B será chamado e não A. Como isso não está sujeito ao polimorfismo, a chamada ts()em A nunca será redirecionada para a de B.

A palavra final- chave impedirá que o método seja oculto. Portanto, eles não podem ser ocultados e uma tentativa de fazer isso resultará em um erro do compilador.

Espero que isto ajude.

NawaMan
fonte
30
Talvez para terminar sua resposta, o que é correto, acredito, o problema aqui é basicamente uma mensagem de erro incorreta do compilador: ele deve indicar que B não pode ocultar ts () em A. Declarar que um método estático final está declarando que não pode ser oculto.
23411 Sean Owen
2
@ Sean Owen: Eu também acho. O termo 'ocultar' é usado até no Java Specification; portanto, por que não usá-lo na mensagem do compilador.
NawaMan 16/11/2009
2
Por que isso é mesmo um recurso? Em que contexto seria útil?
user253751
teste de classe pública {final estático public void main (String ... srik) {System.out.println ("In main method"); ts (); } public static void ts () {Criança c = nova Criança (); c.ts (); System.out.println ("Teste ts"); }} public class Child estende Test {public static void ts () {System.out.println ("Child ts"); }} Oi Você pode me explicar o que acontece nesse cenário?
srikanth r
@SeanOwen Também não acho que isso esteja correto, o compilador deve dizer que, uma vez que A#tsestá sendo herdado e já existe um método B, simplesmente ter dois métodos com a mesma assinatura e um modificador diferente ( final) não funcionaria como sobrecargas. . Eu gostaria de poder pensar em uma mensagem simples para isso, porém
Eugene
13

métodos estáticos não podem ser substituídos

isso não é exatamente verdade. O código de exemplo realmente significa que o método ts em B oculta o método ts em A. Portanto, não é exatamente anulado. Em Javaranch, há uma boa explicação.

Vincent Ramdhanie
fonte
4
É verdade que não é preciso. métodos estáticos não podem ser substituídos, mas podem ser ocultos se você os estiver chamando em uma referência de instância em vez do nome da classe.
John Mercier
1
Infelizmente seu link não está mais funcionando. É possível consertar isso?
Mathias Bader
O link JavaRanch não está funcionando, mas pesquisando as palavras-chave apareceu este link no rancho código
Sundeep
Editei a postagem substituindo o link morto pelo link publicado pela Sundeep.
MC Emperor
10

Os métodos estáticos pertencem à classe, não à instância.

A.ts()e B.ts()sempre serão métodos separados.

O verdadeiro problema é que o Java permite chamar métodos estáticos em um objeto de instância. Métodos estáticos com a mesma assinatura da classe pai são ocultos quando chamados de uma instância da subclasse. No entanto, você não pode substituir / ocultar os métodos finais .

Você pensaria que a mensagem de erro usaria a palavra oculta em vez de substituída ...

Powerlord
fonte
6

Você pode encontrar-se na posição de pensar em finalizar um método estático, considerando o seguinte:

Tendo as seguintes classes:

class A {
    static void ts() {
        System.out.print("A");
    }
}
class B extends A {
    static void ts() {
        System.out.print("B");
    }
}

Agora, a maneira 'correta' de chamar esses métodos seria

A.ts();
B.ts();

o que resultaria, ABmas você também poderia chamar os métodos nas instâncias:

A a = new A();
a.ts();
B b = new B();
b.ts();

o que resultaria ABtambém.

Agora considere o seguinte:

A a = new B();
a.ts();

isso seria impresso A. Isso pode surpreendê-lo, pois você está realmente tendo um objeto de classe B. Mas como você está chamando a partir de uma referência do tipo A, ele chamará A.ts(). Você pode imprimir Bcom o seguinte código:

A a = new B();
((B)a).ts();

Nos dois casos, o objeto que você possui é realmente da classe B. Mas, dependendo do ponteiro que aponta para o objeto, você chamará o método de Aou deB .

Agora, digamos que você é o desenvolvedor da classe Ae deseja permitir a subclassificação. Mas você realmente deseja um método ts(), sempre que chamado, mesmo de uma subclasse, que é o que você deseja que ele faça e que não seja oculto por uma versão da subclasse. Então você pode fazer isso finale impedir que ele fique oculto na subclasse. E você pode ter certeza de que o código a seguir chamará o método da sua classe A:

B b = new B();
b.ts();

Ok, admitidamente, isso é de alguma forma construído, mas pode fazer sentido para alguns casos.

Você não deve chamar métodos estáticos nas instâncias, mas diretamente nas classes - então você não terá esse problema. Também o IntelliJ IDEA, por exemplo, mostrará um aviso, se você chamar um método estático em uma instância e também se você finalizar um método estático.

Mathias Bader
fonte
0

O método ts () em B não substitui o método ts () em A; simplesmente é outro método. A classe B não vê o método ts () em A, pois é estático; portanto, pode declarar seu próprio método chamado ts ().

No entanto, se o método for final, o compilador perceberá que existe um método ts () em A que não deve ser substituído em B.

amischiefr
fonte
Eu não acho que isso explique por que 'final' de repente significa que esses métodos não podem coexistir. Como você diz, sem 'final', não há problema. Você diz que não está substituindo, mas depois diz que o problema é que B não pode substituir o método de A.
23815 Sean Owen
Certamente, afirmo que B não vê o método ts () em A (está 'oculto'), mas o modificador final não 'oculta' métodos de classes que estendem outro. Mas eh, ok.
Amischiefr 16/11/2009
0

Eu acho que o erro de compilação foi bastante enganador aqui. Não deveria ter dito "o método substituído é final estático.", Mas deveria ter dito "o método substituído é final.". O modificador estático é irrelevante aqui.

BalusC
fonte
1
Então você considera o método estático em B substituindo o método em A?
precisa
@KorayTugay Eu me pergunto se o compilador primeiro olha para o método potencialmente substituível (ignora a estática por um momento), vê final em falha. Apenas um palpite embora
Eugene
Balus Acho que esta é a única resposta de baixa qualidade que você tem no StackOverflow. Considerando todas as suas respostas excepcionais, esta não lhes pertence. @BalusC
Koray Tugay
@KorayTugay: naquela época eu não tinha reputação suficiente para comentar :) Se você enviar um ping, eu excluirei a resposta, sem problemas.
BalusC
0

Um método estático não pode ser substituído em Java, diferentemente dos métodos não estáticos. Mas eles são herdados como membros de dados estáticos e não estáticos. É por isso que um método não estático com o mesmo nome não pode ser criado na classe pai

class Writer { 
    public static void doo(){
        System.out.println("sth");
    } 
}
class Author extends Writer{ 
    public void doo(){
        System.out.println("ok"); // error overridden method is static
    }
}

A finalpalavra-chave garante que o corpo do método específico seja executado sempre que uma chamada para o método. Agora, se um método estático for criado na classe filho com o mesmo nome e for feita uma chamada para o método, o método na subclasse será executado, o que não deve ser o caso se final for prefixado antes do nome do método estático na classe pai . Portanto, a palavra-chave final restringe a criação do método com o mesmo nome na classe filho.

Pratik Gupta
fonte