¿Por qué Java no permite anular métodos estáticos?
static override (22)
¿Por qué no es posible anular métodos estáticos?
Si es posible, por favor use un ejemplo.
Bueno ... la respuesta es NO si piensas desde la perspectiva de cómo debería comportarse un método anulado en Java. Pero, no obtiene ningún error de compilación si intenta anular un método estático. Eso significa que, si intentas anularlo, Java no te impide hacer eso; pero ciertamente no obtienes el mismo efecto que obtienes con los métodos no estáticos. La anulación en Java simplemente significa que el método en particular se llamará en función del tipo de tiempo de ejecución del objeto y no del tipo de tiempo de compilación del mismo (que es el caso de los métodos estáticos sustituidos). Bien ... ¿alguna conjetura por la razón por la que se comportan de forma extraña? Debido a que son métodos de clase y, por lo tanto, el acceso a ellos siempre se resuelve durante el tiempo de compilación solo utilizando la información de tipo de tiempo de compilación. Acceder a ellos usando referencias de objetos es solo una libertad adicional otorgada por los diseñadores de Java y ciertamente no deberíamos pensar en detener esa práctica solo cuando la restringen :-)
Ejemplo : tratemos de ver qué sucede si intentamos anular un 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();
// ...
}
}
Salida : -
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod
Observe la segunda línea de la salida. Si se hubiera reemplazado el staticMethod, esta línea debería haber sido idéntica a la tercera línea, ya que estamos invocando el ''staticMethod ()'' en un objeto de Runtime Type como ''SubClass'' y no como ''SuperClass''. Esto confirma que los métodos estáticos siempre se resuelven utilizando solo su información de tipo de tiempo de compilación.
En Java (y muchos lenguajes OOP, pero no puedo hablar por todos; y algunos no tienen estática), todos los métodos tienen una firma fija: los parámetros y los tipos. En un método virtual, el primer parámetro está implícito: una referencia al objeto en sí y cuando se llama desde dentro del objeto, el compilador agrega this
automáticamente.
No hay diferencia para los métodos estáticos, todavía tienen una firma fija. Sin embargo, al declarar el método estático, ha declarado explícitamente que el compilador no debe incluir el parámetro objeto implícito al comienzo de esa firma. Por lo tanto, cualquier otro código que llame a esto no debe intentar poner una referencia a un objeto en la pila . Si lo hiciera, entonces la ejecución del método no funcionaría, ya que los parámetros estarían en el lugar incorrecto (desplazados en uno) en la pila.
Debido a esta diferencia entre los dos; los métodos virtuales siempre tienen una referencia al objeto de contexto (es decir, this
), por lo que es posible hacer referencia a cualquier elemento del montón que pertenezca a esa instancia del objeto. Pero con los métodos estáticos, ya que no se pasa una referencia, ese método no puede acceder a ninguna variable y método del objeto ya que el contexto no se conoce.
Si desea que Java cambie la definición para que se pase un contexto de objeto para cada método, estático o virtual, en esencia, solo tendría métodos virtuales.
Como alguien le preguntó en un comentario a la operación: ¿cuál es su razón y propósito para querer esta función?
No conozco mucho a Ruby, como lo mencionó el OP, hice algunas investigaciones. Veo que en Ruby las clases son realmente un tipo especial de objeto y uno puede crear (incluso dinámicamente) nuevos métodos. Las clases son objetos de clase completa en Ruby, no están en Java. Esto es algo que tendrá que aceptar cuando trabaje con Java (o C #). Estos no son lenguajes dinámicos, aunque C # está agregando algunas formas de dinámico. En realidad, Ruby no tiene métodos "estáticos" por lo que pude encontrar; en ese caso, estos son métodos en el objeto de clase singleton. Luego, puede anular este singleton con una nueva clase y los métodos en el objeto de la clase anterior llamarán a los definidos en la nueva clase (¿correcto?). Por lo tanto, si llama a un método en el contexto de la clase original, aún así solo ejecutará las estadísticas originales, pero al llamar a un método en la clase derivada, llamará a los métodos ya sea desde la clase principal o la subclase. Interesante y puedo ver algo de valor en eso. Se necesita un patrón de pensamiento diferente.
Ya que estás trabajando en Java, necesitarás adaptarte a esa forma de hacer las cosas. ¿Por qué hicieron esto? Bueno, probablemente para mejorar el rendimiento en el momento basado en la tecnología y la comprensión que estaba disponible. Los lenguajes informáticos están en constante evolución. Vuelve lo suficientemente lejos y no hay tal cosa como OOP. En el futuro, habrá otras nuevas ideas.
EDITAR : Otro comentario. Ahora que veo las diferencias y como desarrollador de Java / C #, puedo entender por qué las respuestas que recibe de los desarrolladores de Java pueden ser confusas si proviene de un lenguaje como Ruby. Los métodos static
Java no son lo mismo que class
métodos de class
Ruby. Los desarrolladores de Java tendrán dificultades para entender esto, al igual que los que trabajan principalmente con un lenguaje como Ruby / Smalltalk. Puedo ver cómo esto también sería muy confuso por el hecho de que Java también usa el "método de clase" como otra forma de hablar sobre métodos estáticos, pero Ruby usa este mismo término de manera diferente. Java no tiene métodos de clase de estilo Ruby (lo siento); Ruby no tiene métodos estáticos de estilo Java, que en realidad son solo funciones de estilo de procedimiento antiguas, como se encuentra en C.
Por cierto, gracias por la pregunta! Hoy aprendí algo nuevo sobre métodos de clase (estilo Ruby).
En general, no tiene sentido permitir "anular" los métodos estáticos, ya que no habría una buena manera de determinar a cuál llamar en tiempo de ejecución. Tomando el ejemplo del empleado, si llamamos a RegularEmployee.getBonusMultiplier (), ¿qué método se supone que se debe ejecutar?
En el caso de Java, se podría imaginar una definición de lenguaje en la que es posible "anular" los métodos estáticos siempre que se llamen a través de una instancia de objeto. Sin embargo, todo lo que haría sería volver a implementar métodos de clase regulares, agregando redundancia al lenguaje sin agregar ningún beneficio.
En realidad estábamos equivocados.
A pesar de que Java no le permite anular los métodos estáticos de forma predeterminada, si examina detenidamente la documentación de las clases de clase y método en Java, aún puede encontrar una manera de emular los métodos estáticos que se anulan siguiendo la siguiente solución:
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
lo que estábamos tratando de lograr :)
Incluso si declaramos la tercera variable Carl como RegularEmployee y le asignamos una instancia de SpecialEmployee, seguiremos teniendo el método de call of RegularEmployee en el primer caso y el método de la llamada SpecialEmployee en el segundo caso
RegularEmployee Carl = new SpecialEmployee();
System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());
solo mira la consola de salida:
0.02
0.03
;)
La anulación del método es posible mediante el envío dinámico , lo que significa que el tipo declarado de un objeto no determina su comportamiento, sino su tipo de tiempo de ejecución:
Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"
Aunque tanto lassie
como kermit
están declarados como objetos de tipo Animal
, su comportamiento (método .speak()
) varía porque el envío dinámico solo bind el método call .speak()
a una implementación en tiempo de ejecución, no en tiempo de compilación.
Ahora, aquí es donde la palabra clave static
comienza a tener sentido: la palabra "estática" es un antónimo de "dinámico". Entonces, la razón por la que no puede anular los métodos estáticos es porque no hay un envío dinámico en los miembros estáticos, porque estáticamente significa literalmente "no dinámico". Si se enviaran dinámicamente (y, por lo tanto, se podría anular), la palabra clave static
ya no tendría sentido.
La anulación depende de tener una instancia de una clase. El punto del polimorfismo es que puede subclasificar una clase y los objetos que implementan esas subclases tendrán diferentes comportamientos para los mismos métodos definidos en la superclase (y reemplazados en las subclases). Un método estático no está asociado con ninguna instancia de una clase, por lo que el concepto no es aplicable.
Hubo dos consideraciones que impulsaron el diseño de Java que impactaron esto. Una de ellas era la preocupación por el rendimiento: había habido muchas críticas a Smalltalk acerca de que era demasiado lenta (la recolección de basura y las llamadas polimórficas eran parte de eso) y los creadores de Java estaban decididos a evitar eso. Otra fue la decisión de que la audiencia objetivo para Java eran los desarrolladores de C ++. Hacer que los métodos estáticos funcionen de la manera en que lo hicieron tuvo la ventaja de estar familiarizado con los programadores de C ++ y también fue muy rápido, ya que no es necesario esperar hasta el tiempo de ejecución para averiguar a qué método llamar.
La respuesta corta es: es completamente posible, pero Java no lo hace.
Aquí hay un código que ilustra el estado actual de las cosas en Java:
Archivo 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();
}
}
Archivo 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();
}
}
Si ejecutas esto (lo hice en una Mac, desde Eclipse, usando Java 1.6) obtienes:
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.
Aquí, los únicos casos que pueden ser una sorpresa (y de los cuales trata la pregunta) parecen ser el primer caso:
"El tipo de tiempo de ejecución no se usa para determinar a qué métodos estáticos se llama, incluso cuando se llama con una instancia de objeto ( obj.staticMethod()
)".
y el último caso:
"Al llamar a un método estático desde dentro de un método de objeto de una clase, el método estático elegido es el que se puede acceder desde la propia clase y no desde la clase que define el tipo de tiempo de ejecución del objeto".
Llamando con una instancia de objeto
La llamada estática se resuelve en tiempo de compilación, mientras que una llamada de método no estático se resuelve en tiempo de ejecución. Tenga en cuenta que aunque los métodos estáticos se heredan (de padres), no se anulan (por hijo). Esto podría ser una sorpresa si esperabas lo contrario.
Llamar desde un método de objeto
Las llamadas de método de objeto se resuelven utilizando el tipo de tiempo de ejecución, pero las llamadas de método estático ( clase ) se resuelven utilizando el tipo de tiempo de compilación (declarado).
Cambiando las reglas
Para cambiar estas reglas, para que la última llamada en el ejemplo llamada Child.printValue()
, las llamadas estáticas deban ser provistas con un tipo en tiempo de ejecución, en lugar de que el compilador resuelva la llamada en tiempo de compilación con la clase declarada del objeto (o contexto). Las llamadas estáticas podrían entonces usar la jerarquía de tipo (dinámica) para resolver la llamada, tal como lo hacen las llamadas de método de objeto hoy.
Esto sería fácilmente factible (si cambiamos Java: -O), y no es del todo irrazonable, sin embargo, tiene algunas consideraciones interesantes.
La consideración principal es que debemos decidir qué llamadas de método estático deben hacer esto.
En este momento, Java tiene esta "peculiaridad" en el lenguaje por el cual las llamadas obj.staticMethod()
son reemplazadas por las llamadas ObjectClass.staticMethod()
(normalmente con una advertencia). [ Nota: ObjectClass
es el tipo de obj
de tiempo de compilación]. Estos serían buenos candidatos para anular de esta manera, tomando el tipo de obj
de tiempo de ejecución.
Si lo hiciéramos, los cuerpos de los métodos serían más difíciles de leer: las llamadas estáticas en una clase principal podrían potencialmente ser "re-enrutadas" dinámicamente . Para evitar esto, tendríamos que llamar al método estático con un nombre de clase, y esto hace que las llamadas se resuelvan más obviamente con la jerarquía de tipo de tiempo de compilación (como ahora).
Las otras formas de invocar un método estático son más difíciles: this.staticMethod()
debería significar lo mismo que obj.staticMethod()
, tomando el tipo de tiempo de ejecución de this
. Sin embargo, esto podría causar algunos dolores de cabeza con los programas existentes, que llaman a métodos estáticos (aparentemente locales) sin decoración (lo que es posiblemente equivalente a this.method()
).
Entonces, ¿qué pasa con las llamadas sin adornos staticMethod()
? Sugiero que hagan lo mismo que hoy, y utilicen el contexto de clase local para decidir qué hacer. De lo contrario se produciría una gran confusión. Por supuesto, significa que method()
significaría this.method()
si el method
fuera un method
no estático, y ThisClass.method()
si el method
fuera un método estático. Esta es otra fuente de confusión.
Otras Consideraciones
Si cambiamos este comportamiento (y hacemos llamadas estáticas potencialmente dinámicamente no locales), probablemente querríamos revisar el significado de final
, private
y protected
como calificadores en static
métodos static
de una clase. Entonces, todos tendremos que acostumbrarnos al hecho de que private static
métodos private static
public final
private static
y public final
no se anulan, por lo que se pueden resolver de manera segura en el momento de la compilación y son "seguros" para leer como referencias locales.
Los métodos estáticos son tratados como globales por la JVM, no están vinculados a una instancia de objeto en absoluto.
Conceptualmente podría ser posible si pudiera invocar métodos estáticos desde objetos de clase (como en lenguajes como Smalltalk) pero no es el caso en Java.
EDITAR
Puedes sobrecargar el método estático, eso está bien. Pero no puede anular un método estático, porque la clase no es un objeto de primera clase. Puede usar la reflexión para obtener la clase de un objeto en tiempo de ejecución, pero el objeto que obtiene no es paralelo a la jerarquía de clases.
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
Puedes reflexionar sobre las clases, pero se detiene ahí. No clazz1.staticMethod()
un método estático utilizando clazz1.staticMethod()
, sino utilizando MyClass.staticMethod()
. Un método estático no está vinculado a un objeto y, por lo tanto, no existe una noción de this
ni de super
en un método estático. Un método estático es una función global; como consecuencia, tampoco existe una noción de polimorfismo y, por lo tanto, la anulación del método no tiene sentido.
Pero esto podría ser posible si MyClass
fuera un objeto en el tiempo de ejecución en el que invoca un método, como en Smalltalk (o quizás JRuby como sugiere un comentario, pero no sé nada de JRuby).
Oh sí ... una cosa más. Puede invocar un método estático a través de un objeto obj1.staticMethod()
pero ese azúcar realmente sintáctico para MyClass.staticMethod()
y debe evitarse. Por lo general, plantea una advertencia en el IDE moderno. No sé por qué alguna vez permitieron este atajo.
Personalmente creo que esto es una falla en el diseño de Java. Sí, sí, entiendo que los métodos no estáticos se adjuntan a una instancia, mientras que los métodos estáticos se adjuntan a una clase, etc. Sin embargo, considere el siguiente 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 no funcionará como cabría esperar. A saber, SpecialEmployee''s obtiene un bono del 2% al igual que los empleados regulares. Pero si elimina las "estáticas", entonces SpecialEmployee obtendrá un bono del 3%.
(Es cierto que este ejemplo es un estilo de codificación deficiente, ya que en la vida real es probable que desee que el multiplicador de bonificación se encuentre en una base de datos en lugar de un código rígido. Pero eso es solo porque no quise atascar el ejemplo con mucho de código irrelevante al punto.)
Me parece bastante plausible que quieras hacer que getBonusMultiplier static. Quizás desee poder mostrar el multiplicador de bonificación para todas las categorías de empleados, sin necesidad de tener una instancia de un empleado en cada categoría. ¿Cuál sería el punto de buscar tales ejemplos de ejemplos? ¿Qué sucede si estamos creando una nueva categoría de empleados y todavía no tenemos empleados asignados? Esto es bastante lógico una función estática.
Pero no funciona.
Y sí, sí, puedo pensar en varias formas de volver a escribir el código anterior para que funcione. Mi punto no es que cree un problema sin solución, sino que crea una trampa para el programador incauto, porque el lenguaje no se comporta como creo que una persona razonable esperaría.
Tal vez si intentara escribir un compilador para un lenguaje OOP, vería rápidamente por qué implementarlo para que las funciones estáticas puedan ser anuladas sería difícil o imposible.
O quizás hay alguna buena razón por la que Java se comporta de esta manera. ¿Alguien puede señalar una ventaja de este comportamiento, alguna categoría de problema que se simplifique con esto? Quiero decir, no solo señale la especificación del lenguaje Java y diga "vea, esto está documentado cómo se comporta". Yo sé eso. ¿Pero hay una buena razón por la que DEBE comportarse de esta manera? (Además de lo obvio, "hacer que funcione bien fue demasiado difícil" ...)
Actualizar
@VicKirk: Si quiere decir que este es un "mal diseño" porque no se ajusta a la forma en que Java maneja las estadísticas, mi respuesta es: "Bueno, claro, por supuesto". Como dije en mi post original, no funciona. Pero si quiere decir que es un mal diseño en el sentido de que habría un error fundamental en un lenguaje en el que funcionaba, es decir, en que las estadísticas podrían ser anuladas al igual que las funciones virtuales, esto introduciría de alguna manera una ambigüedad o sería imposible implementar de manera eficiente o algo así, yo respondo: "¿Por qué? ¿Qué tiene de malo el concepto?"
Creo que el ejemplo que doy es algo muy natural que quiero hacer. Tengo una clase que tiene una función que no depende de ningún dato de instancia, y que podría querer llamar de forma bastante independiente, además de querer llamar desde un método de instancia. ¿Por qué no debería funcionar esto? Me he encontrado con esta situación un buen número de veces en los últimos años. En la práctica lo evito haciendo la función virtual, y luego creando un método estático cuyo único propósito en la vida es ser un método estático que pase la llamada al método virtual con una instancia ficticia. Eso parece una manera muy indirecta de llegar allí.
Sí. Prácticamente Java permite anular el método estático, y no teóricamente, si anula un método estático en Java, se compilará y ejecutará sin problemas, pero perderá el polimorfismo, que es la propiedad básica de Java. Leerá en todas partes que no es posible que usted mismo intente compilar y ejecutar. obtendrás tu respuesta por ejemplo, si tiene Clase Animal y un método estático eat () y Anula ese método estático en su Subclase, se le llama Perro. Luego, cuando quiera que asigne un objeto Perro a una referencia de animal y llame a eat () de acuerdo con Java Dog''s eat (), se debería haber llamado, pero en modo estático Overriding Animals ''eat () se llamará.
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 acuerdo con el principio de polimorfismo de Java, la salida debe ser la de Dog Eating
.
Pero el resultado fue diferente porque para admitir Polymorphism Java usa Late Binding, lo que significa que los métodos se llaman solo en tiempo de ejecución pero no en el caso de métodos estáticos. En los métodos estáticos, el compilador llama a los métodos en el momento de la compilación en lugar del tiempo de ejecución, por lo que obtenemos métodos según la referencia y no según el objeto, una referencia que contiene. ''t
El siguiente código muestra que es posible:
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();
}
}
La respuesta a esta pregunta es simple, el método o la variable marcada como estática pertenece solo a la clase, por lo que ese método estático no se puede heredar en la subclase porque pertenece solo a la súper clase
¿Qué bien hará para anular los métodos estáticos? No puedes llamar a métodos estáticos a través de una instancia.
MyClass.static1()
MySubClass.static1() // If you overrode, you have to call it through MySubClass anyway.
EDITAR: Parece que a través de una desafortunada supervisión en el diseño del lenguaje, puede llamar a métodos estáticos a través de una instancia. Generalmente nadie hace eso. Mi error.
Ahora, viendo las respuestas anteriores, todos saben que no podemos anular los métodos estáticos, pero no se debe malinterpretar el concepto de acceso a métodos estáticos desde subclase .
Podemos acceder a métodos estáticos de superclase con referencia a subclases si este método estático no ha sido ocultado por un nuevo método estático definido en la subclase.
Por ejemplo, vea el siguiente código:
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");
// }
}
Salida:-
SuperClass saying Hello
Consulte Java oracle docs y busque Lo que puede hacer en una subclase para obtener detalles sobre cómo ocultar métodos estáticos en la subclase.
Gracias
Al anular, podemos crear una naturaleza polimórfica según el tipo de objeto. El método estático no tiene relación con el objeto. Así que Java no puede soportar el método estático que reemplaza.
Anulando, logras polimorfismo dinámico. Cuando se dice que sustituye los métodos estáticos, las palabras que intenta utilizar son contradictorias.
El estático dice: el tiempo de compilación, la invalidación se usa para el polimorfismo dinámico. Ambos son de naturaleza opuesta, y por lo tanto no se pueden usar juntos.
El comportamiento polimórfico dinámico se produce cuando un programador utiliza un objeto y accede a un método de instancia. JRE asignará diferentes métodos de instancia de diferentes clases según el tipo de objeto que esté utilizando.
Cuando diga que reemplaza métodos estáticos, los métodos estáticos a los que accederemos utilizando el nombre de la clase, que se vinculará en el momento de la compilación, por lo que no existe el concepto de vincular los métodos en tiempo de ejecución con métodos estáticos. Por lo tanto, el término "anulación" de los métodos estáticos en sí mismo no tiene ningún significado.
Nota: incluso si accede a un método de clase con un objeto, el compilador aún java es lo suficientemente inteligente como para descubrirlo y hará un enlace estático.
Aquí hay una explicación simple. Un método estático está asociado con una clase, mientras que un método de instancia está asociado con un objeto en particular. Las anulaciones permiten llamar a las diferentes implementaciones de los métodos anulados asociados con el objeto en particular. Por lo tanto, es contraintuitivo anular el método estático que, en primer lugar, ni siquiera está asociado con los objetos, sino con la propia clase. Por lo tanto, los métodos estáticos no se pueden anular en función de qué objeto lo está llamando, siempre se asociará con la clase donde se creó.
La anulación en Java simplemente significa que el método en particular se llamará en función del tipo de tiempo de ejecución del objeto y no del tipo en tiempo de compilación (que es el caso de los métodos estáticos anulados). Como los métodos estáticos son métodos de clase, no son métodos de instancia, por lo que no tienen nada que ver con el hecho de que la referencia apunta a qué Objeto o instancia, ya que, debido a la naturaleza del método estático, pertenece a una clase específica. Puede volver a declararlo en la subclase, pero esa subclase no sabrá nada sobre los métodos estáticos de la clase principal porque, como dije, es específico solo para esa clase en la que se ha declarado. Acceder a ellos usando referencias de objetos es solo una libertad adicional otorgada por los diseñadores de Java y ciertamente no deberíamos pensar en detener esa práctica solo cuando restringen más detalles y ejemplos.http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html
La anulación está reservada para que los miembros de la instancia admitan el comportamiento polimórfico. Los miembros de la clase estática no pertenecen a una instancia en particular. en su lugar, los miembros estáticos pertenecen a la clase y, como resultado, no se admite la anulación porque las subclases solo heredan miembros de instancias públicas y protegidas y no miembros estáticos. Es posible que desee definir una fábrica de interiores y una fábrica de investigación y / o patrones de diseño de estrategias para evaluar un enfoque alternativo.
Me gusta y doble el comentario de Jay ( https://.com/a/2223803/1517187 ).
Estoy de acuerdo en que este es el mal diseño de Java.
Muchos otros lenguajes admiten métodos estáticos de anulación, como vemos en comentarios anteriores. Siento que Jay también ha venido a Java desde Delphi como yo.
Delphi (Object Pascal) fue el primer lenguaje que implementó OOP.
Es obvio que muchas personas tenían experiencia con ese idioma, ya que en el pasado era el único idioma para escribir productos de GUI comerciales. Y, sí, podríamos en Delphi reemplazar métodos estáticos. En realidad, los métodos estáticos en Delphi se denominan "métodos de clase", mientras que Delphi tenía el concepto diferente de "métodos estáticos de Delphi", que eran métodos con enlace temprano. Para anular los métodos que tuvo que usar el enlace tardío, declare la directiva "virtual". Así que fue muy conveniente e intuitivo y esperaría esto en Java.
Solución fácil: usar instancia de singleton. Permitirá anulaciones y herencias.
En mi sistema, tengo la clase SingletonsRegistry, que devuelve la instancia de la clase pasada. Si no se encuentra la instancia, se crea.
Clase 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!";
}
}
Un método estático, variable, bloque o clase anidada pertenece a toda la clase en lugar de a un objeto.
Un método en Java se utiliza para exponer el comportamiento de un objeto / clase. Aquí, como el método es estático (es decir, el método estático se usa para representar el comportamiento de una clase solamente), cambiar / anular el comportamiento de toda la clase violará el fenómeno de uno de los pilares fundamentales de la programación orientada a objetos, es decir, la alta cohesión . (recuerde que un constructor es un tipo especial de método en Java).
Alta cohesión : una clase debe tener un solo rol. Por ejemplo: una clase de automóvil debe producir solo objetos de automóvil y no bicicletas, camiones, aviones, etc. Pero la clase de Automóvil puede tener algunas características (comportamiento) que solo pertenecen a sí mismas.
Por lo tanto, al diseñar el lenguaje de programación java. Los diseñadores de lenguajes pensaron permitir a los desarrolladores mantener algunos comportamientos de una clase para sí mismos solo haciendo un método de naturaleza estática.
El siguiente código de pieza intenta anular el método estático, pero no encontrará ningún error de compilación.
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;
}}
Esto se debe a que, aquí no estamos anulando un método sino que simplemente lo estamos re-declarando . Java permite la re-declaración de un método (estático / no estático).
Eliminar la palabra clave estática del método getVehileNumber () de la clase Car resultará en un error de compilación, ya que estamos tratando de cambiar la funcionalidad del método estático que pertenece solo a la clase Vehicle.
Además, si getVehileNumber () se declara como final , el código no se compilará, ya que la palabra clave final impide que el programador vuelva a declarar el método.
public static final int getVehileNumber() {
return VIN; }
En general, esto es hasta diseñadores de software para dónde usar los métodos estáticos. Personalmente, prefiero usar métodos estáticos para realizar algunas acciones sin crear ninguna instancia de una clase. En segundo lugar, para ocultar el comportamiento de una clase del mundo exterior.