¿Por qué llamar a un método estático a través de una instancia no es un error para el compilador de Java?

StackOverflow https://stackoverflow.com/questions/610458

  •  03-07-2019
  •  | 
  •  

Pregunta

Estoy seguro de que todos ustedes conocen el comportamiento al que me refiero, código como:

Thread thread = new Thread();
int activeCount = thread.activeCount();

provoca una advertencia del compilador. ¿Por qué no es un error?

EDIT:

Para ser claros: la pregunta no tiene nada que ver con los subprocesos. Me doy cuenta de que a menudo se dan ejemplos de hilos cuando se analiza esto debido a la posibilidad de ensuciar realmente las cosas con ellos. Pero realmente el problema es que tal uso es siempre sin sentido y usted no puede (de manera competente) escribir esa llamada y decirlo en serio. Cualquier ejemplo de este tipo de método de llamada sería barmy. Aquí hay otro:

String hello = "hello";
String number123AsString = hello.valueOf(123);

Lo que hace que parezca que cada instancia de String viene con un " String valueOf (int i) " método.

¿Fue útil?

Solución

Básicamente, creo que los diseñadores de Java cometieron un error cuando diseñaron el lenguaje, y es demasiado tarde para solucionarlo debido a los problemas de compatibilidad involucrados. Sí, puede llevar a un código muy engañoso. Sí, deberías evitarlo. Sí, debe asegurarse de que su IDE esté configurado para tratarlo como un error, OMI. Si alguna vez diseñas un idioma, tómalo en cuenta como un ejemplo del tipo de cosas que debes evitar :)

Solo para responder al punto de DJClayworth, esto es lo que está permitido en C #:

public class Foo
{
    public static void Bar()
    {
    }
}

public class Abc
{
    public void Test()
    {
        // Static methods in the same class and base classes
        // (and outer classes) are available, with no
        // qualification
        Def();

        // Static methods in other classes are available via
        // the class name
        Foo.Bar();

        Abc abc = new Abc();

        // This would *not* be legal. It being legal has no benefit,
        // and just allows misleading code
        // abc.Def();
    }

    public static void Def()
    {
    }
}

¿Por qué creo que es engañoso? Porque si miro el código someVariable.SomeMethod () , espero que use el valor de someVariable . Si SomeMethod () es un método estático, esa expectativa no es válida; El código me está engañando. ¿Cómo puede ser eso una cosa buena ?

Curiosamente, Java no le permitirá usar una variable potencialmente no inicializada para llamar a un método estático, a pesar de que la única información que va a usar es el tipo declarado de la variable. Es un lío inconsistente e inútil. ¿Por qué permitirlo?

EDITAR: Esta edición es una respuesta a la respuesta de Clayton, que afirma que permite la herencia de métodos estáticos. No lo hace Los métodos estáticos simplemente no son polimórficos. Aquí hay un programa corto pero completo para demostrar que:

class Base
{
    static void foo()
    {
        System.out.println("Base.foo()");
    }
}

class Derived extends Base
{
    static void foo()
    {
        System.out.println("Derived.foo()");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Base b = new Derived();
        b.foo(); // Prints "Base.foo()"
        b = null;
        b.foo(); // Still prints "Base.foo()"
    }
}

Como puede ver, el valor de tiempo de ejecución de b se ignora por completo.

Otros consejos

¿Por qué debería ser un error? La instancia tiene acceso a todos los métodos estáticos. Los métodos estáticos no pueden cambiar el estado de la instancia (intentar es un error de compilación).

El problema con el conocido ejemplo que das es muy específico de hilos , no de llamadas a métodos estáticos. Parece que está obteniendo el activeCount () para el subproceso al que hace referencia el thread , pero realmente está obteniendo el recuento para el subproceso de la llamada. Este es un error lógico que usted como programador está cometiendo. Emitir una advertencia es lo que debe hacer el compilador en este caso. Depende de usted prestar atención a la advertencia y corregir su código.

EDITAR: Me doy cuenta de que la sintaxis del lenguaje es lo que permite que usted escriba código engañoso, pero recuerde que el compilador y sus advertencias también forman parte del lenguaje. El lenguaje le permite hacer algo que el compilador considera dudoso, pero le da una advertencia para asegurarse de que está consciente de que podría causar problemas.

Ya no pueden convertirlo en un error, debido a todo el código que ya existe.

Estoy contigo en que debería ser un error. Tal vez debería haber una opción / perfil para que el compilador actualice algunas advertencias a errores.

Actualización: cuando introdujeron la palabra clave assert en 1.4, que tiene problemas de compatibilidad potenciales similares con el código antiguo, lo hicieron solo está disponible si establece explícitamente el modo de origen en " 1.4 " . Supongo que uno podría cometer un error en un nuevo modo de fuente " java 7 " Pero dudo que lo hicieran, teniendo en cuenta que todos los problemas que causaría. Como han señalado otros, no es estrictamente necesario evitar que escriba un código confuso. Y los cambios de idioma en Java deben limitarse a lo estrictamente necesario en este punto.

Respuesta corta: el idioma lo permite, por lo que no es un error.

Probablemente por la misma lógica que hace que esto no sea un error:

public class X
{
    public static void foo()
    {
    }

    public void bar()
    {
        foo(); // no need to do X.foo();
    }
}

Lo realmente importante, desde la perspectiva del compilador, es que sea capaz de resolver símbolos. En el caso de un método estático, necesita saber en qué clase buscarlo, ya que no está asociado con ningún objeto en particular. Los diseñadores de Java obviamente decidieron que, dado que podían determinar la clase de un objeto, también podían resolver la clase de cualquier método estático para ese objeto desde cualquier instancia del objeto. Eligen permitir que esto, influido, tal vez, por la observación de @ TofuBeer, le dé al programador algo de comodidad. Otros diseñadores de idiomas han hecho diferentes elecciones. Probablemente me hubiera caído en el último campo, pero para mí no es un gran problema. Probablemente permitiría el uso que menciona @TofuBeer, pero habiendo permitido que mi posición para no permitir el acceso desde una variable de instancia sea menos sostenible.

No es un error porque es parte de la especificación, pero obviamente estás preguntando sobre la razón, que todos podemos adivinar.

Mi conjetura es que la fuente de esto es en realidad permitir que un método en una clase invoque un método estático en la misma clase sin la molestia. Dado que llamar a x () es legal (incluso sin el nombre de clase propia), llamar a this.x () también debe ser legal, y por lo tanto, llamar a través de cualquier objeto también se hizo legal.

Esto también ayuda a animar a los usuarios a convertir las funciones privadas en estáticas si no cambian el estado.

Además, los compiladores generalmente tratan de evitar la declaración de errores cuando no hay forma de que esto pueda conducir a un error directo. Dado que un método estático no cambia el estado ni se preocupa por el objeto que invoca, no causa un error real (solo confusión) para permitir esto. Una advertencia basta.

El propósito de la referencia de variable de instancia es solo proporcionar el tipo que encierra la estática. Si observa el código de byte que invoca una estática a través de instance.staticMethod o EnclosingClass.staticMethod produce el mismo bytecode del método estático de invocación. No aparece ninguna referencia a la instancia.

La respuesta también es porque está ahí, bueno, simplemente es. Siempre y cuando uses la clase. y no a través de una instancia, ayudará a evitar confusiones en el futuro.

Probablemente puede cambiarlo en su IDE (en Preferencias de Eclipse - > Java - > Compiler - > Errors / Warnings)

No hay opción para ello. En java (como muchos otros idiomas), puede tener acceso a todos los miembros estáticos de una clase a través de su nombre de clase u objeto de instancia de esa clase. Eso dependería de usted y de su caso y la solución de software, la que debería usar que le proporcione mayor legibilidad.

Solo considero esto:

instanceVar.staticMethod();

ser taquigrafía para esto:

instanceVar.getClass().staticMethod();

Si siempre tuvieras que hacer esto:

SomeClass.staticMethod();

entonces no podría aprovechar la herencia para métodos estáticos.

Es decir, al llamar al método estático a través de la instancia, no es necesario saber qué clase concreta es la instancia en tiempo de compilación, solo que implemente staticMethod () en algún lugar a lo largo de la cadena de herencia.

EDITAR: Esta respuesta es incorrecta. Ver comentarios para más detalles.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top