¿Por qué Java no permite anular los métodos estáticos?

534

¿Por qué no es posible anular los métodos estáticos?

Si es posible, utilice un ejemplo.

Sgokhales
fuente
3
La mayoría de los lenguajes OOP no permiten esto.
jmucchiello
77
@jmucchiello: mira mi respuesta. Estaba pensando lo mismo que usted, pero luego aprendí sobre los métodos de 'clase' de Ruby / Smalltalk y, por lo tanto, hay otros lenguajes verdaderos de OOP que hacen esto.
Kevin Brock
55
@jmucchiello la mayoría del lenguaje OOP no es lenguaje OOP real (pienso en Smalltalk)
mathk
Consulte también stackoverflow.com/q/370962/632951
Pacerier el
1
puede deberse a que Java resuelve llamadas a métodos estáticos en tiempo de compilación. Entonces, incluso si ha escrito Parent p = new Child()y luego p.childOverriddenStaticMethod()el compilador lo resolverá Parent.childOverriddenStaticMethod()mirando el tipo de referencia.
Manoj

Respuestas:

494

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 comportamientos diferentes para los mismos métodos definidos en la superclase (y anulados 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: hubo muchas críticas a Smalltalk por ser demasiado lento (la recolección de basura y las llamadas polimórficas son parte de eso) y los creadores de Java estaban decididos a evitar eso. Otra fue la decisión de que el público objetivo de Java fueran los desarrolladores de C ++. Hacer que los métodos estáticos funcionen de la manera en que lo hicieron tuvo el beneficio de la familiaridad para los programadores de C ++ y también fue muy rápido, porque no hay necesidad de esperar hasta el tiempo de ejecución para descubrir qué método llamar.

Nathan Hughes
fuente
18
... pero solo "correcto" en Java. Por ejemplo, el equivalente de Scala de "clases estáticas" (que se llaman objects) permite la sobrecarga de métodos.
32
Objective-C también permite anular los métodos de clase.
Richard
11
Hay una jerarquía de tipos en tiempo de compilación y una jerarquía de tipos en tiempo de ejecución. Tiene mucho sentido preguntar por qué una llamada a un método estático no aprovecha la jerarquía de tipos de tiempo de ejecución en aquellas circunstancias en las que existe. En Java esto sucede cuando se llama a un método estático desde un objeto ( obj.staticMethod()), que está permitido y utiliza los tipos de tiempo de compilación. Cuando la llamada estática está en un método no estático de una clase, el objeto "actual" podría ser un tipo derivado de la clase, pero los métodos estáticos definidos en los tipos derivados no se consideran (están en el tipo de tiempo de ejecución jerarquía).
Steve Powell
18
Debería haber dejado claro: es no cierto que el concepto no es aplicable .
Steve Powell
13
Esta respuesta, si bien es correcta, se asemeja más a "cómo es" en lugar de cómo debería ser o más exactamente cómo podría ser para cumplir con las expectativas del OP y, por lo tanto, aquí, yo y los demás. No hay una razón concreta para no permitir la anulación de métodos estáticos que no sean "así es como es". Creo que es una falla personalmente.
RichieHH
186

Personalmente, creo que esto es un defecto 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. Aún así, 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 es de esperar. A saber, los empleados especiales obtienen una bonificación del 2% al igual que los empleados regulares. Pero si elimina los "estáticos", los empleados especiales recibirán un bono del 3%.

(Es cierto que este ejemplo es un estilo de codificación deficiente en el sentido de que en la vida real es probable que desee que el multiplicador de bonificación esté en una base de datos en algún lugar en lugar de estar codificado. Pero eso es solo porque no quería empantanar el ejemplo con mucho de código irrelevante para el punto.)

Me parece bastante plausible que desee hacer que getBonusMultiplier sea estático. 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 ejemplos de este tipo? ¿Qué sucede si estamos creando una nueva categoría de empleados y todavía no tenemos empleados asignados? Esto es lógicamente una función estática.

Pero no funciona.

Y sí, sí, se me ocurren muchas formas de reescribir el código anterior para que funcione. Mi punto no es que crea 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.

Quizás si tratara de escribir un compilador para un lenguaje OOP, vería rápidamente por qué implementarlo para que las funciones estáticas puedan ser reemplazadas sería difícil o imposible.

O tal vez hay una buena razón por la cual Java se comporta de esta manera. ¿Alguien puede señalar una ventaja para este comportamiento, alguna categoría de problema que se facilita con esto? Quiero decir, no solo me señales la especificación del lenguaje Java y digas "mira, 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 del 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 cómo Java maneja las estadísticas, mi respuesta es: "Bueno, claro, por supuesto". Como dije en mi publicación original, no funciona. Pero si quiere decir que es un mal diseño en el sentido de que habría algo fundamentalmente incorrecto con un lenguaje en el que esto funcionara, es decir, donde las estadísticas podrían anularse al igual que las funciones virtuales, eso de alguna manera introduciría una ambigüedad o sería imposible implementar de manera eficiente o algo así, 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 razonablemente podría querer llamar independientemente de una instancia, además de querer llamar desde un método de instancia. ¿Por qué esto no debería funcionar? Me he encontrado con esta situación varias veces a lo largo de los años. En la práctica, lo evito haciendo que la función sea 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. Esa parece ser una forma indirecta de llegar allí.

Arrendajo
fuente
11
@Bemrose: Pero ese es mi punto: ¿Por qué no se me debe permitir hacer eso? Tal vez mi concepto intuitivo de lo que debe hacer un "estático" es diferente del suyo, pero básicamente pienso en un estático como un método que PUEDE ser estático porque no usa ningún dato de instancia, y que DEBERÍA ser estático porque es posible que desee llamarlo independientemente de una instancia. Una estática está claramente vinculada a una clase: espero que Integer.valueOf esté vinculado a Integers y Double.valueOf esté vinculado a Dobles.
Jay
99
@ewernli y Bemrose: Sí, sí, así es. No estoy debatiendo eso. Como el código en mi ejemplo no funciona, por supuesto no trataría de escribirlo. Mi pregunta es por qué es así. (Me temo que esto se está convirtiendo en una de esas conversaciones en las que simplemente no nos estamos comunicando. "Disculpe Sr. Vendedor, ¿puedo obtener uno de estos en rojo?" "No, cuesta $ 5". "Sí, sé que cuesta $ 5, pero ¿puedo obtener uno rojo? "" Señor, acabo de decirle que cuesta $ 5 "." Está bien, sé el precio, pero estaba preguntando por el color "." ¡Ya le dije el precio! " etc.)
Jay
66
Creo que, en última instancia, este código es confuso. Considere si la instancia se pasa como un parámetro. Entonces está diciendo que la instancia de tiempo de ejecución debe dictar qué método estático se llama. Eso básicamente hace que una jerarquía completamente separada sea paralela a la instancia existente. ¿Qué sucede si una subclase define la misma firma de método como no estática? Creo que las reglas complicarían las cosas. Precisamente este tipo de complicaciones del lenguaje es lo que Java intenta evitar.
Yishai
66
@Yishai: RE "La instancia de tiempo de ejecución determina qué método estático se llama": Exactamente. No veo por qué no puedes hacer nada con una estática que puedes hacer con una virtual. "Jerarquía separada": Yo diría: Haz que sea parte de la misma jerarquía. ¿Por qué no se incluyen las estadísticas en la misma jerarquía? "La subclase define la misma firma no estática": supongo que sería ilegal, al igual que es ilegal, por ejemplo, que una subclase anule una función con una firma idéntica pero un tipo de retorno diferente, o que no arroje todas las excepciones que el padre tira, o para tener un alcance más estrecho.
Jay
28
Creo que Jay tiene razón: me sorprendió también cuando descubrí que no podía anularse la estática. En parte porque si tengo A con el método someStatic()y B extiende A, luego se B.someMethod() une al método en A. Si posteriormente agrego someStatic()a B, el código de llamada aún se invoca A.someStatic()hasta que vuelva a compilar el código de llamada. También me sorprendió que bInstance.someStatic()use el tipo declarado de bInstance, no el tipo de tiempo de ejecución porque se une al compilar y no al enlace, por lo que A bInstance; ... bInstance.someStatic()invoca A.someStatic () si existe B.someStatic ().
Lawrence Dol
42

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 ejecuta esto (lo hice en una Mac, desde Eclipse, usando Java 1.6) obtendrá:

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 podrían ser una sorpresa (y sobre los que trata la pregunta) parecen ser el primer caso:

"El tipo de tiempo de ejecución no se utiliza para determinar qué métodos estáticos se llaman, incluso cuando se llama con una instancia de objeto ( obj.staticMethod())".

y el último caso:

"Cuando se llama a un método estático desde un método de objeto de una clase, el método estático elegido es el accesible desde la propia clase y no desde la clase que define el tipo de tiempo de ejecución del objeto".

Llamar 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 (del padre), no se anulan (por el niño). Esto podría ser una sorpresa si esperabas lo contrario.

Llamar desde un método de objeto

Las llamadas a métodos de objeto se resuelven usando el tipo de tiempo de ejecución, pero las llamadas a métodos estáticos (de clase ) se resuelven usando el tipo de tiempo de compilación (declarado).

Cambiando las reglas

Para cambiar estas reglas, para que la última llamada en el ejemplo llamado Child.printValue(), las llamadas estáticas tengan que proporcionarse 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 usar la jerarquía de tipos (dinámicos) para resolver la llamada, tal como lo hacen hoy las llamadas a métodos de objetos.

Esto sería fácilmente factible (si cambiamos Java: -O), y no es en absoluto irrazonable, sin embargo, tiene algunas consideraciones interesantes.

La consideración principal es que debemos decidir qué llamadas a métodos estáticos deberían hacer esto.

Por el momento, Java tiene esta "peculiaridad" en el lenguaje por el cual las obj.staticMethod()llamadas se reemplazan por ObjectClass.staticMethod()llamadas (normalmente con una advertencia). [ Nota: ObjectClass es el tipo de tiempo de compilación de obj.] Estos serían buenos candidatos para anular de esta manera, tomando el tipo de tiempo de ejecución de obj.

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 primaria podrían potencialmente ser " reenrutadas " 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 complicadas: this.staticMethod()deberían significar lo mismo que obj.staticMethod()tomar 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 (que posiblemente sea equivalente a this.method()).

Entonces, ¿qué pasa con las llamadas sin adornos staticMethod()? Sugiero que hagan lo mismo que hoy y usen el contexto de clase local para decidir qué hacer. De lo contrario, se produciría una gran confusión. Por supuesto, significa que eso method()significaría this.method()si methodfuera un método no estático y ThisClass.method()si methodfuera un método estático. Esta es otra fuente de confusión.

Otras Consideraciones

Si cambiamos este comportamiento (e hicimos llamadas estáticas potencialmente dinámicamente no locales), probablemente querríamos revisar el significado de final, privatey protectedcomo calificadores, en los staticmétodos de una clase. Tendríamos entonces todos tenemos que acostumbrarse al hecho de que private staticy public finalmétodos no son anulados, y por tanto pueden ser resueltos de manera segura en tiempo de compilación, y es "seguro" para que lea como referencias locales.

Steve Powell
fuente
"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 primaria podrían potencialmente ser" reenrutadas "dinámicamente". Es cierto, pero esto es exactamente lo que sucede con las llamadas a funciones no estáticas comunes ahora. Esto se promociona habitualmente como una característica positiva para las funciones virtuales ordinarias, no es un problema.
Jay
25

En realidad estábamos equivocados.
A pesar de que Java no le permite anular los métodos estáticos de manera predeterminada, si mira detenidamente la documentación de las clases Class y Method en Java, aún puede encontrar una manera de emular los métodos estáticos reemplazando 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, todavía tendremos que llamar al método RegularEmployee en el primer caso y llamar al método 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

;)

Patlatus
fuente
99
Sí, la reflexión es prácticamente lo único que se puede hacer, pero la pregunta no es exactamente esto, aunque es útil tenerla aquí
Mr_and_Mrs_D
1
Esta respuesta es el hack más grande que he visto hasta ahora en todos los temas de Java. Fue divertido leerlo todavía :)
Andrejs
19

La JVM trata los métodos estáticos como globales, no están vinculados a una instancia de objeto en absoluto.

Conceptualmente podría ser posible si pudieras llamar a 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, 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 allí. No invocas un método estático usando clazz1.staticMethod(), sino usando MyClass.staticMethod(). Un método estático no está vinculado a un objeto y, por lo tanto, no existe una noción thisni superun 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 MyClassfuera un objeto en tiempo de ejecución en el que invoque un método, como en Smalltalk (o tal vez 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 MyClass.staticMethod()debe evitarse. Por lo general, genera una advertencia en el IDE moderno. No sé por qué alguna vez permitieron este atajo.

ewernli
fuente
55
Incluso muchos lenguajes modernos como Ruby tienen métodos de clase y permiten anularlos.
Chandra Sekar
3
Las clases existen como objetos en Java. Ver la clase "Clase". Puedo decir myObject.getClass () y me devolverá una instancia del objeto de clase apropiado.
Jay
55
Solo obtiene una "descripción" de la clase, no la clase en sí. Pero la diferencia es sutil.
ewernli
Todavía tiene clase pero está oculta en la VM (cerca del cargador de clases), el usuario casi no tiene acceso a ella.
mathk
Para usarlo clazz2 instanceof clazz1correctamente, puede usarlo class2.isAssignableFrom(clazz1), lo que creo que sería cierto en su ejemplo.
Simon Forsberg
14

La anulación de métodos es posible mediante el despacho 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 ambos lassiey kermitse declaran como objetos de tipo Animal, su comportamiento (método .speak()) varía porque el despacho dinámico solo vinculará la llamada .speak()al método a una implementación en tiempo de ejecución, no en tiempo de compilación.

Ahora, aquí es donde la staticpalabra clave 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 despacho dinámico en miembros estáticos, porque estático literalmente significa "no dinámico". Si se despacharan dinámicamente (y, por lo tanto, podrían anularse), la staticpalabra clave ya no tendría sentido.

Richard JP Le Guen
fuente
11

Si. 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 intentar compilar y ejecutar. Obtendrás tu respuesta. por ejemplo, si tiene Class Animal y un método estático eat () y anula ese método estático en su subclase, vamos a llamarlo Dog. Entonces, cuando sea que asigne un objeto Perro a una Referencia de animal y llame a eat () de acuerdo con Java, se debería haber llamado eat () pero en estática 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

Según el principio de polimorfismo de Java, la salida debería ser Dog Eating.
Pero el resultado fue diferente porque para admitir el polimorfismo, Java utiliza enlace tardío, lo que significa que los métodos se invocan solo en el tiempo de ejecución, pero no en el caso de los 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 de acuerdo con la referencia y no de acuerdo con el objeto, una referencia que contiene, por eso. 't.

Shubham Soni
fuente
3
Es una mala práctica llamar a métodos estáticos desde un objeto.
Dmitry Zagorulkin
6

la anulación está reservada para miembros de instancia para admitir el comportamiento polimórfico. los miembros de la clase estática no pertenecen a una instancia particular. en cambio, los miembros estáticos pertenecen a la clase y, como resultado, no se admite la anulación porque las subclases solo heredan miembros de instancia públicos y protegidos y no miembros estáticos. Es posible que desee definir una fábrica de interfaz e investigación y / o patrones de diseño de estrategia para evaluar un enfoque alternativo.

Atenas Holloway
fuente
1
¿No leyó ninguna de las otras respuestas que ya cubren esto y dejó en claro que estas no son razones suficientes para descontar las estadísticas primordiales a nivel conceptual. Sabemos que no funciona. Es perfectamente "limpia desear la anulando de métodos estáticos y de hecho es posible en muchos otros idiomas.
RichieHH
Richard, supongamos por un minuto que cuando respondí a esta pregunta hace 4 años, la mayoría de estas respuestas no se habían publicado, ¡así que no seas tonto! No hay necesidad de afirmar que no leí con cuidado. Además, ¿no leyó que solo estamos discutiendo la anulación con respecto a Java? A quién le importa lo que es posible en otros idiomas. Es irrelevante Ve a troll a otro lado. Su comentario no agrega nada de valor a este hilo.
Athens Holloway
6

En Java (y en 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í mismo y cuando se llama desde dentro del objeto, el compilador agrega automáticamente this.

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 de 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, la ejecución del método no funcionaría ya que los parámetros estarían en el lugar incorrecto, desplazado por 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 cosa dentro del montón que pertenezca a esa instancia del objeto. Pero con los métodos estáticos, dado que no se pasa ninguna referencia, ese método no puede acceder a ninguna variable de objeto y métodos 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, entonces, en esencia, solo tendría métodos virtuales.

Como alguien 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, ya que esto fue mencionado por el OP, investigué un poco. 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ámicas. 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 del objeto de clase anterior llamarán a los definidos en la nueva clase (¿correcto?). Entonces, si llamó a un método en el contexto de la clase original, todavía solo ejecutaría las estadísticas originales, pero llamar a un método en la clase derivada, llamaría a los métodos desde el padre o la subclase. Interesante y puedo ver algo de valor en eso. Se necesita un patrón de pensamiento diferente.

Como está trabajando en Java, tendrá que adaptarse a esa forma de hacer las cosas. ¿Por qué hicieron esto? Bueno, probablemente para mejorar el rendimiento en el momento en función de la tecnología y la comprensión que estaba disponible. Los lenguajes informáticos están en constante evolución. Regrese lo suficientemente lejos y no hay tal cosa como OOP. En el futuro, habrá otras ideas nuevas.

EDITAR : Otro comentario. Ahora que veo las diferencias y, como desarrollador de Java / C #, puedo entender por qué las respuestas que obtienes de los desarrolladores de Java pueden ser confusas si vienes de un lenguaje como Ruby. Los staticmétodos Java no son lo mismo que los classmétodos Ruby . Los desarrolladores de Java tendrán dificultades para entender esto, al igual que aquellos 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 este mismo término es usado de manera diferente por Ruby. 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 funciones de estilo de procedimiento antiguas, como se encuentra en C.

Por cierto, ¡gracias por la pregunta! Hoy aprendí algo nuevo para mí sobre los métodos de clase (estilo Ruby).

Kevin Brock
fuente
1
Por supuesto, no hay ninguna razón por la que Java no pueda pasar un objeto "Clase" como parámetro oculto a los métodos estáticos. Simplemente no fue diseñado para hacerlo.
jmucchiello
6

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 del compilador si intenta anular un método estático. Eso significa que, si intentas anular, Java no te impide hacerlo; pero ciertamente no obtienes el mismo efecto que obtienes para los métodos no estáticos. Reemplazar en Java simplemente significa que se llamará a un método en particular basado en el tipo de tiempo de ejecución del objeto y no en el tipo de tiempo de compilación del mismo (que es el caso con los métodos estáticos anulados). De acuerdo ... ¿adivinan por qué se comportan de manera 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 usando la información del tipo de tiempo de compilación.

Ejemplo : intentemos 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 anulado 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 Tipo de tiempo de ejecución como 'Subclase' y no como 'Superclase'. Esto confirma que los métodos estáticos siempre se resuelven utilizando únicamente su información de tipo de tiempo de compilación.

Rupesh Yadav
fuente
5

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 de Empleado, si llamamos a RegularEmployee.getBonusMultiplier (), ¿qué método se supone que debe ejecutarse?

En el caso de Java, uno podría imaginar una definición de lenguaje donde es posible 'anular' los métodos estáticos siempre que se invoquen 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 realmente ningún beneficio.

Lars
fuente
2
Intuitivamente, creo que debería funcionar como una función virtual. Si B extiende A y tanto A como B tienen funciones virtuales llamadas doStuff, el compilador sabe que las instancias de A deberían usar A.doStuff y las instancias de B deberían usar B.doStuff. ¿Por qué no puede hacer lo mismo con las funciones estáticas? Después de todo, el compilador sabe de qué clase es una instancia cada objeto.
Jay
Erm ... Jay, un método estático no necesita ser llamado (generalmente no es) en una instancia ...
Meriton
2
@meriton, pero luego es aún más fácil, ¿no? Si se llama a un método estático usando un nombre de clase, usaría el método apropiado para la clase.
CPerkins
Pero entonces, ¿qué es lo que está haciendo por ti? Si llama a A.doStuff (), debe usar la versión que se anuló en "B extiende A" o la versión que se anuló en "C extiende A". Y si tiene C o B, entonces está llamando a esas versiones de todos modos ... no es necesario anular.
PSpeed
@meriton: Es cierto que un método estático generalmente no se llama con una instancia, ¡pero supongo que es porque tal llamada no hace nada útil dado el diseño actual de Java! Estoy sugiriendo que un diseño alternativo podría haber sido una mejor idea. Por cierto, en un sentido muy real, las funciones estáticas se llaman con una instancia de manera bastante rutinaria: cuando se llama a la función estática desde una función virtual. Entonces implícitamente obtienes this.function (), es decir, la instancia actual.
Jay
5

Al anular podemos crear una naturaleza polimórfica dependiendo del tipo de objeto. El método estático no tiene relación con el objeto. Entonces, Java no puede soportar la anulación del método estático.

DesarrolladorArnab
fuente
5

Me gusta y doblo el comentario de Jay ( https://stackoverflow.com/a/2223803/1517187 ).
Estoy de acuerdo en que este es el mal diseño de Java.
Muchos otros idiomas admiten métodos estáticos de anulación, como vemos en comentarios anteriores. Siento que Jay también llegó 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 GUI comerciales. Y, sí, podríamos en Delphi anular los métodos estáticos. En realidad, los métodos estáticos en Delphi se llaman "métodos de clase", mientras que Delphi tenía el concepto diferente de "métodos estáticos Delphi", que eran métodos con enlace temprano. Para anular los métodos que tenía que usar el enlace tardío, declare la directiva "virtual". Así que fue muy conveniente e intuitivo y esperaría esto en Java.

Patlatus
fuente
3

¿De qué servirá para anular los métodos estáticos? No puede 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. Culpa mía.

fastcodejava
fuente
8
"No puede llamar a métodos estáticos a través de una instancia" En realidad, una de las peculiaridades de Java es que PUEDE llamar a métodos estáticos a través de una instancia, aunque sea una idea extremadamente mala.
Powerlord
1
De hecho, Java permite el acceso a miembros estáticos a través de instancias: consulte Variable estática en Java
Richard JP Le Guen
Sin embargo, un IDE moderno adecuado generará una advertencia cuando lo haga, por lo que al menos puede detectarlo mientras Oracle puede mantener la compatibilidad con versiones anteriores.
Gimby
1
No hay nada conceptualmente malo en poder llamar a un método estático a través de una instancia. Esto es una disputa por el amor de Dios. ¿Por qué algo como una instancia de Date NO llama a sus propios métodos estáticos pasando datos de instancia a la función a través de la interfaz de llamada?
RichieHH
@RichieHH Eso no es lo que están discutiendo. La pregunta es por qué está permitido llamar variable.staticMethod(), en lugar de Class.staticMethod(), dónde variablees una variable con tipo declarado Class. Estoy de acuerdo en que es un mal diseño del lenguaje.
fishinear
3

Reemplazar en Java simplemente significa que el método particular se llamaría 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 con 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 qué referencia apunta a qué objeto o instancia, porque 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ífica solo de la clase en la que se ha declarado. Acceder a ellos utilizando referencias de objetos es solo una libertad adicional dada por los diseñadores de Java y ciertamente no debemos pensar en detener esa práctica solo cuando la restringen más detalles y ejemplos. http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html

faisalbhagat
fuente
3

Al anular, se logra un polimorfismo dinámico. Cuando dice que anula los métodos estáticos, las palabras que está tratando de usar son contradictorias.

La estática dice: tiempo de compilación, la anulació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 usa 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 dice que anula los métodos estáticos, a los métodos estáticos accederemos utilizando el nombre de la clase, que se vinculará en tiempo de compilación, por lo que no existe el concepto de vincular métodos en tiempo de ejecución con métodos estáticos. Por lo tanto, el término "sobrescribir" 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, aún el compilador de Java es lo suficientemente inteligente como para descubrirlo y realizará enlaces estáticos.

usuario1923551
fuente
Esto no es cierto en muchas instancias en las que, en tiempo de ejecución, el método estático se llama desde una instancia de la clase que lo contiene y, por lo tanto, es perfectamente factible determinar qué instancias de la función invocar.
RichieHH
static no significa tiempo de compilación, static significa que está vinculado a la clase en lugar de a cualquier objeto específico. tiene mucho más sentido que crear una fábrica de clases, excepto que la estática Box.createBoxtiene más sentido que eso BoxFactory.createBox, y es un patrón inevitable cuando se necesita verificar la construcción de errores sin lanzar excepciones (los constructores no pueden fallar, solo pueden matar el proceso / lanzamiento excepción), mientras que un método estático puede devolver nulo en caso de error, o incluso aceptar devoluciones de llamada de éxito / error para escribir algo como hastebin.com/codajahati.java .
Dmitry
2

La respuesta a esta pregunta es simple, el método o la variable marcada como estática pertenece solo a la clase, de modo que el método estático no se puede heredar en la subclase porque pertenecen solo a la superclase.

g1ji
fuente
1
Hola, G4uKu3_Gaurav. Gracias por decidir contribuir. Sin embargo, generalmente esperamos respuestas más largas y detalladas que esta.
DJClayworth
@DJClayworth debe seguir este enlace para obtener una respuesta detallada geeksforgeeks.org/…
g1ji
Gracias por el enlace. En realidad, estoy aquí para ayudar a los recién llegados al sitio, para explicar cómo funciona el sitio para aquellos que no están acostumbrados, no porque necesite una respuesta a la pregunta.
DJClayworth
1

Solución fácil: use instancia singleton. Permitirá anulaciones y herencia.

En mi sistema, tengo la clase SingletonsRegistry, que devuelve la instancia para la clase aprobada. 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!";
  }

}
Raivo Fishmeister
fuente
Muy bien, es la primera vez que escucho sobre el lenguaje de programación Haxe :)
cyc115
1
Esto se implementa mucho mejor en Java como método estático en la clase misma; Singleton.get(). El registro es simplemente una sobrecarga, y excluye GC en las clases.
Lawrence Dol
Tienes razón, es una solución clásica. No recuerdo exactamente por qué elegí el registro, probablemente tenía algún marco de pensamiento que condujo a este resultado.
Raivo Fishmeister
1

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, 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 le pertenecen.

Por lo tanto, al diseñar el lenguaje de programación java. Los diseñadores de lenguaje pensaron permitir a los desarrolladores mantener algunos comportamientos de una clase para sí mismos solo al hacer que un método sea 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 volviendo a declarar . Java permite la nueva 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 dará como resultado 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 restringe al programador de volver a declarar el método.

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

En general, esto depende de los diseñadores de software sobre 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.

Rohit Gaikwad
fuente
1

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 la implementación diferente de los métodos anulados asociados con el objeto en particular. Por lo tanto, es contra-intuitivo anular el método estático que ni siquiera está asociado con los objetos, sino con la clase misma en primer lugar. Por lo tanto, los métodos estáticos no se pueden anular según el objeto que lo llama, siempre se asociará con la clase donde se creó.

Rafsan Mobasher
fuente
¿Cómo es contrario a la intuición tener una public abstract IBox createBox();interfaz interna de IBox? Box puede implementar IBox para anular createBox y hacer que la creación del objeto dé como resultado un IBox válido; de lo contrario, devolverá un valor nulo. Los constructores no pueden devolver "nulo" y, por lo tanto, se ve obligado a (1) usar excepciones EN TODAS PARTES (lo que hacemos ahora), o (2) crear clases de fábrica que hagan lo que dije antes, pero de una manera que no tiene sentido para principiantes ni expertos de Java (que también hacemos ahora). Los métodos estáticos no implementados resuelven esto.
Dmitry
-1

Ahora, viendo las respuestas anteriores, todos saben que no podemos anular los métodos estáticos, pero uno no debe malinterpretar el concepto de acceder a métodos estáticos desde la subclase .

Podemos acceder a métodos estáticos de superclase con referencia de subclase si este método estático no ha sido ocultado por un nuevo método estático definido en 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 los documentos de Oracle oracle y busque Qué puede hacer en una subclase para obtener detalles sobre cómo ocultar métodos estáticos en una subclase.

Gracias

amandeep1991
fuente
-3

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();

}   

} 
pila de pop
fuente
1
No, no lo hace; el tipo estático declarado de no osmes . OverridenStaticMethOverrideStaticMeth
Lawrence Dol
2
Además, trataría de evitar usar tanta Metanfetamina al programar <gran sonrisa>;
Lawrence Dol