Comportamiento del método estático final.

123

He estado jugando con modificadores con método estático y encontré un comportamiento extraño.

Como sabemos, los métodos estáticos no pueden anularse, ya que están asociados con la clase en lugar de la instancia.

Entonces, si tengo el fragmento de abajo, se compila bien

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

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

Pero si incluyo el modificador final al método estático en la clase A, la compilación falla ts () en B no puede anular ts () en A; El método reemplazado es estático final .

¿Por qué sucede esto cuando el método estático no se puede anular en absoluto?

Harish
fuente
2
parece extraño, +1 para la pregunta, pero hasta ahora ninguna de las respuestas es satisfactoria.
Rakesh Juyal
1
No se anula. Todavía está allí en A.ts ().
Alex Feinman el

Respuestas:

166

Los métodos estáticos no se pueden anular, pero se pueden ocultar. El ts()método de B no anula (no está sujeto a polimorfismo) el ts()de A pero lo ocultará. Si llama ts()a B (NO A.ts()o B.ts()... solo ts()), se llamará a B y no a A. Dado que esto no está sujeto a polimorfismo, la llamada ts()en A nunca se redirigirá a la de B.

La palabra clave finaldeshabilitará la ocultación del método. Por lo tanto, no se pueden ocultar y un intento de hacerlo generará un error de compilación.

Espero que esto ayude.

NawaMan
fuente
30
Tal vez para finalizar su respuesta, que es lo correcto, creo que el problema aquí es básicamente un mensaje de error del compilador: debería decir que B no puede ocultar ts () en A. Declarar que un método estático final es declarar que no se puede ocultar.
Sean Owen el
2
@Sean Owen: Yo también lo creo. El término 'ocultar' incluso se usa en la especificación de Java, entonces, ¿por qué no usarlo en el mensaje del compilador?
NawaMan el
2
¿Por qué es esto incluso una característica? ¿En qué contexto sería útil?
user253751
prueba de clase pública {main estático public void main (String ... srik) {System.out.println ("En el método principal"); ts (); } public static void ts () {Child c = new Child (); c.ts (); System.out.println ("Prueba ts"); }} clase pública Child extiende Test {public static void ts () {System.out.println ("Child ts"); }} Hola, ¿puedes explicarme qué sucede en este escenario
Srikanth r
@SeanOwen No creo que esto sea correcto tampoco, el compilador debería decir que dado que A#tsse está heredando y que tal método ya existe B, simplemente tener dos métodos con la misma firma y un modificador diferente ( final) no funcionaría como sobrecargas. Sin embargo, desearía poder pensar en un mensaje simple para esto
Eugene
13

los métodos estáticos no se pueden anular

Esto no es exactamente cierto. El código de ejemplo realmente significa que el método ts en B oculta el método ts en A. Por lo tanto, no se anula exactamente. En Javaranch hay una buena explicación.

Vincent Ramdhanie
fuente
44
Es cierto, simplemente no es preciso. Los métodos estáticos no pueden anularse, pero pueden ocultarse si los llama a una referencia de instancia en lugar del nombre de la clase.
John Mercier
1
Lamentablemente, su enlace ya no funciona. ¿Es posible arreglar esto?
Mathias Bader
El enlace javaranch no funciona, pero Google las palabras clave activaron este enlace en el rancho de código
Sundeep
He editado la publicación reemplazando el enlace muerto por el enlace publicado por Sundeep.
MC Emperor
10

Los métodos estáticos pertenecen a la clase, no a la instancia.

A.ts()y B.ts()siempre serán métodos separados.

El verdadero problema es que Java le permite llamar a métodos estáticos en un objeto de instancia. Los métodos estáticos con la misma firma de la clase padre se ocultan cuando se llama desde una instancia de la subclase. Sin embargo, no puede anular / ocultar los métodos finales .

Pensarías que el mensaje de error usaría la palabra oculta en lugar de anulada ...

Powerlord
fuente
6

Puede encontrarse en la posición de pensar en hacer un método estático final, considerando lo siguiente:

Tener las siguientes clases:

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

Ahora la forma 'correcta' de llamar a estos métodos sería

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

lo que resultaría en, ABpero también podría llamar a los métodos en instancias:

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

lo que resultaría ABtambién.

Ahora considere lo siguiente:

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

eso se imprimiría A. Eso podría sorprenderlo ya que en realidad está teniendo un objeto de clase B. Pero como lo está llamando desde una referencia de tipo A, llamará A.ts(). Puede imprimir Bcon el siguiente código:

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

En ambos casos, el objeto que tiene es en realidad de la clase B. Pero dependiendo del puntero que apunta al objeto, llamará al método desde Ao desde B.

Ahora digamos que usted es el desarrollador de la clase Ay desea permitir la subclasificación. Pero realmente desea un método ts(), siempre que se lo llame, incluso desde una subclase, que hace lo que quiere que haga y que no esté oculto por una versión de subclase. Entonces podría hacerlo finaly evitar que se oculte en la subclase. Y puede estar seguro de que el siguiente código llamará al método desde su clase A:

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

Ok, admitidamente, eso está construido de alguna manera, pero podría tener sentido para algunos casos.

No debe llamar a métodos estáticos en instancias sino directamente en las clases, entonces no tendrá ese problema. También IntelliJ IDEA, por ejemplo, le mostrará una advertencia, si llama a un método estático en una instancia y también si hace que un método estático sea final.

Mathias Bader
fuente
0

El método ts () en B no anula el método ts () en A, simplemente es otro método. La clase B no ve el método ts () en A ya que es estático, por lo tanto, puede declarar su propio método llamado ts ().

Sin embargo, si el método es final, entonces el compilador detectará que hay un método ts () en A que no debe anularse en B.

amischiefr
fuente
No creo que esto explique por qué 'final' de repente significa que estos métodos no pueden coexistir. Como dices, sin 'final', no hay problema. Usted dice que no está anulando, pero luego dice que el problema es que B no puede anular el método de A.
Sean Owen el
Claro que sí, afirmo que B no ve el método ts () en A (está 'oculto'), pero el modificador final no 'oculta' los métodos de las clases que extienden otro. Pero eh, ok.
amischiefr
0

Creo que el error de compilación fue bastante engañoso aquí. No debería haber dicho "el método reemplazado es estático final", sino que debería haber dicho "el método reemplazado es final". El modificador estático es irrelevante aquí.

BalusC
fuente
1
Entonces, ¿considera que el método estático en B anula al de A?
Koray Tugay
@KorayTugay Solo me pregunto si el compilador primero mira el método potencialmente anulable (ignora la estática por un momento), ve el final en fallas. Sin embargo, es una suposición descabellada
Eugene el
Balus Creo que esta es la única respuesta de baja calidad que tiene en StackOverflow. Teniendo en cuenta todas sus respuestas excepcionales, esta no les pertenece. @BalusC
Koray Tugay
@KorayTugay: en ese momento no tenía suficiente reputación para comentar :) Si vuelves a hacer ping, eliminaré la respuesta, no hay problema.
BalusC
0

Un método estático no se puede anular en Java, a diferencia de los métodos no estáticos. Pero se heredan como miembros de datos estáticos y no estáticos. Es por eso que no se puede crear un método no estático con el mismo nombre en la clase primaria

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

La finalpalabra clave asegura que el cuerpo del método específico se ejecute cada vez que se llame al método. Ahora, si se crea un método estático en la clase secundaria con el mismo nombre y se realiza una llamada al método, se ejecuta el método en la subclase, lo que no debería ser el caso si final es prefijado antes del nombre del método estático en la clase primaria . Por lo tanto, la palabra clave final restringe la creación de un método con el mismo nombre en la clase secundaria.

Pratik Gupta
fuente