¿Cuál es la ganancia de declarar un método como estático?

94

Recientemente estuve revisando mis advertencias en Eclipse y me encontré con esta:

advertencia estática

Dará una advertencia al compilador si el método se puede declarar como estático.

[editar] Cita exacta en la ayuda de Eclipse, con énfasis en privado y final:

Cuando está habilitado, el compilador emitirá un error o una advertencia para los métodos que son privados o finales y que se refieren solo a miembros estáticos.

Sí, sé que puedo apagarlo, pero quiero saber la razón para encenderlo.

¿Por qué sería bueno declarar todos los métodos posibles como estáticos?

¿Dará esto algún beneficio de rendimiento? (en un dominio móvil)

Al señalar un método como estático, supongo que muestra que no usa ninguna variable de instancia, por lo tanto, ¿podría moverse a una clase de estilo utils?

Al final del día, ¿debería simplemente desactivar esta opción 'ignorar' o debería corregir las más de 100 advertencias que me ha dado?

¿Crees que estas son solo palabras clave adicionales que ensucian el código, ya que el compilador simplemente insertará estos métodos de todos modos? (como si no declararas todas las variables que puedes finalizar pero podrías ).

Blundell
fuente
No estoy seguro, pero esto podría verse simplemente como una ayuda para la programación. Las advertencias son simplemente indicaciones de cosas a tener en cuenta.
James P.
1
Tengo curiosidad por el tipo de funcionalidad que realizan estos métodos. Tal vez algo no se hizo muy bien si hay tantos de estos.
ArjunShankar
Relacionado: stackoverflow.com/q/790281
Brian Rasmussen

Respuestas:

132

Siempre que escribe un método, cumple un contrato en un ámbito determinado. Cuanto más estrecho sea el alcance, menor será la posibilidad de que escriba un error.

Cuando un método es estático, no puede acceder a miembros no estáticos; por lo tanto, su alcance es más estrecho. Entonces, si no necesita y nunca necesitará (incluso en subclases) miembros no estáticos para cumplir con su contrato, ¿por qué dar acceso a estos campos a su método? Declarar el método staticen este caso permitirá que el compilador verifique que no use miembros que no pretenda usar.

Y además, ayudará a las personas que lean su código a comprender la naturaleza del contrato.

Por eso se considera bueno declarar un método staticcuando en realidad está implementando un contrato estático.

En algunos casos, su método solo significa algo relativo a una instancia de su clase, y sucede que su implementación en realidad no usa ningún campo o instancia no estático. En tales casos, no marcaría el método static.

Ejemplos de dónde no usaría la staticpalabra clave:

  • Un gancho de extensión que no hace nada (pero podría hacer algo con datos de instancia en una subclase)
  • Un comportamiento predeterminado muy simple destinado a ser personalizable en una subclase.
  • Implementación del controlador de eventos: la implementación variará con la clase del controlador de eventos, pero no utilizará ninguna propiedad de la instancia del controlador de eventos.
Samuel Rossille
fuente
1
+1 Se trata de minimizar las oportunidades de dispararse a sí mismo en el pie y reducir cuánto necesita saber para comprender un método.
Peter Lawrey
7
Además, no necesita jugar con la adquisición de una instancia solo para llamar a una función independiente y autónoma.
Marko Topolnik
1
Entonces, en otros mundos, ¿cualquier método que no utilice variables de instancia debería declararse como estático? Siempre pensé que los métodos deberían ser estáticos solo si es deseable (como los métodos de utilidad).
Petr Mensik
18
@PetrMensik Si un privatemétodo pudiera declararse estático, casi invariablemente debería serlo. Para cualquier otro nivel de acceso, hay otros factores a considerar, como el envío dinámico.
Marko Topolnik
1
@JamesPoulson Me refiero al envío de métodos dinámicos, lo que sucede mientras se esfuerza object.method()por seleccionar el método a llamar.
Marko Topolnik
15

Aquí no hay ningún concepto con optimización.

Un staticmétodo se staticdebe a que declaras explícitamente que el método no depende de ninguna instancia de la clase adjunta solo porque no es necesario. Entonces esa advertencia de Eclipse, como se indica en la documentación:

Cuando está habilitado, el compilador emitirá un error o una advertencia para los métodos que son privados o finales y que se refieren solo a miembros estáticos.

Si no necesita ninguna variable de instancia y su método es privado (no se puede llamar desde fuera) o final (no se puede anular), entonces no hay razón para dejar que sea un método normal en lugar de uno estático. Un método estático es intrínsecamente más seguro incluso porque se le permite hacer menos cosas con él (no necesita ninguna instancia, no tiene ningún thisobjeto implícito ).

Jack
fuente
7

No tengo información sobre el rendimiento, supongo que es un poco mejor como mucho, ya que el código no necesita realizar un envío dinámico según el tipo.

Sin embargo, un argumento mucho más fuerte en contra de la refactorización en métodos estáticos es que actualmente el uso de estático se considera una mala práctica. Los métodos / variables estáticos no se integran bien en un lenguaje orientado a objetos y, además, son difíciles de probar correctamente. Esta es la razón por la que algunos lenguajes más nuevos renuncian por completo al concepto de métodos / variables estáticos, o intentan internalizarlo en el lenguaje de una manera que funcione mejor con OO (por ejemplo, Objetos en Scala).

La mayoría de las veces, necesita métodos estáticos para implementar funciones que solo usan parámetros como entrada y producen una salida usando eso (por ejemplo, funciones de utilidad / ayuda) En los lenguajes modernos, hay un concepto de función de primera clase que permite eso, tan estático no es necesario. Java 8 tendrá expresiones lambda integradas, por lo que ya nos estamos moviendo en esta dirección.

Istvan Devai
fuente
7
Los métodos estáticos son triviales de probar (siempre que sean autónomos, especialmente funciones puras, que son el objetivo principal de un método estático). El concepto OO no tiene nada que ofrecer en ese caso. Además, el concepto de función de primera clase tiene muy poco, si es que tiene algo, que ver con el concepto de método estático. Si alguna vez necesita una función de primera clase que haga el trabajo de un método estático existente, es cuestión de un puñado de caracteres implementarla.
Marko Topolnik
5
El comentario de probabilidad probablemente no estaba dirigido directamente al método estático, pero los métodos estáticos son mucho más difíciles de burlar, por lo que complican la prueba de métodos que llaman a métodos estáticos.
Buhb
2
Los métodos estáticos son fáciles de simular si usa un marco de simulación poderoso como JMockit , PowerMock o Groovy .
Jeff Olson
Estos son private staticmétodos para que no tenga que burlarse de ellos
Blundell
3

1. Método de declaraciónstaticproporciona un ligero beneficio de rendimiento, pero lo que es más útil, permite usarlo sin tener una instancia de objeto a mano (piense, por ejemplo, en el método de fábrica o en obtener un singleton). También sirve al propósito documental de contar la naturaleza del método. Este propósito documental no debe ignorarse, ya que da una pista inmediata sobre la naturaleza del método a los lectores del código y a los usuarios de la API y también sirve como una herramienta de pensamiento para el programador original: ser explícito sobre el significado deseado ayuda también piensa con claridad y produce código de mejor calidad (creo que basado en mi experiencia personal, pero las personas son diferentes). Por ejemplo, es lógico y, por lo tanto, deseable distinguir entre métodos que operan en un tipo y métodos que actúan en una instancia del tipo (como lo señalaJon Skeet en su comentario a una pregunta de C # ).

Otro caso de uso más de los staticmétodos es imitar la interfaz de programación de procedimientos. Piense en la java.lang.System.println()clase y sus métodos y atributos. La clase java.lang.Systemse utiliza como un espacio de nombres de agrupación en lugar de un objeto instanciable.

2. ¿Cómo puede Eclipse (o cualquier otra entidad programada o de otro tipo, biocomponible o no biocomponible) saber con certeza qué método podría declararse estático? Incluso si una clase base no accede a variables de instancia o llama a métodos no estáticos, por el mecanismo de herencia las cosas pueden cambiar. Solo si el método no se puede anular heredando la subclase, podemos afirmar con un 100% de certeza que el método realmente se puede declarar static. Anular un método es imposible exactamente en los dos casos de ser

  1. private (ninguna subclase puede usarlo directamente y ni siquiera en principio lo sabe), o
  2. final (incluso si es accesible por la subclase, no hay forma de cambiar el método para hacer referencia a funciones o datos de instancia).

De ahí la lógica de la opción Eclipse.

3. El póster original también pregunta: "¿ Señalar un método como estático, supongo que está mostrando que no usa ninguna variable de instancia, por lo tanto, podría ser movido a una clase de estilo utils? " Este es un muy buen punto. A veces, este tipo de cambio de diseño se indica mediante una advertencia.

Es una opción muy útil, que personalmente me aseguraría de habilitar si tuviera que usar Eclipse y programar en Java.

FooF
fuente
1

Vea la respuesta de Samuel sobre cómo cambia el alcance del método. Supongo que este es el aspecto principal de convertir un método en estático.

También preguntaste sobre el rendimiento:

Puede haber una pequeña ganancia de rendimiento, porque una llamada a un método estático no necesita la referencia implícita "this" como parámetro.

Sin embargo, este impacto en el rendimiento es realmente mínimo. Por lo tanto, se trata del alcance.

Negro
fuente
1

De las pautas de rendimiento de Android:

Prefiera estático sobre virtual Si no necesita acceder a los campos de un objeto, haga que su método sea estático. Las invocaciones serán entre un 15% y un 20% más rápidas. También es una buena práctica, porque puede deducir de la firma del método que llamar al método no puede alterar el estado del objeto.

http://developer.android.com/training/articles/perf-tips.html#PreferStatic

Blundell
fuente
"que llamar al método no puede alterar el estado del objeto" - es increíblemente engañoso. No sé ustedes, pero ciertamente considero que los atributos estáticos de una clase son parte del estado de un objeto.
Adam Parkin
0

Bueno, la documentación de Eclipse dice sobre la advertencia en cuestión:

El método puede ser estático

Cuando está habilitado, el compilador emitirá un error o una advertencia para los métodos que son privados o finales y que se refieren solo a miembros estáticos.

Creo que prácticamente lo dice todo. Si el método es privado y final y solo se refiere a miembros estáticos, el método en cuestión también podría declararse estático y, por lo tanto, hacer evidente que solo pretendemos acceder a contenido estático desde él.

Honestamente, no creo que haya ninguna otra razón misteriosa detrás de esto.

Edwin Dalorzo
fuente
0

Me faltaban algunos números para las diferencias de velocidad. Así que intenté compararlos, lo que resultó no ser tan fácil: ¿el bucle de Java se vuelve más lento después de algunas ejecuciones / falla de JIT?

Finalmente usé Caliper y los resultados son los mismos que ejecutar mis pruebas a mano:

No hay una diferencia medible para llamadas estáticas / dinámicas. Al menos no para Linux / AMD64 / Java7.

Los resultados de Caliper están aquí: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent,scenarioop.vm. CMSLargeSplitSurplusPercent, escenario.vmSpec.options.CMSSmallCoalSurplusPercent, escenario.vmSpec.options.CMSSmallSplitSurplusPercent, escenario.vmSpec.options.FLSLargestBlockCoalesceProximity, escenario.vmSpec.Pec.opciones.

y mis propios resultados son:

Static: 352 ms
Dynamic: 353 ms
Static: 348 ms
Dynamic: 349 ms
Static: 349 ms
Dynamic: 348 ms
Static: 349 ms
Dynamic: 344 ms

La clase Caliper Test fue:

public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark {

    public static void main( String [] args ){

        CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args );
    }

    public int timeAddDynamic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addDynamic( 1, i );
        }
        return r;
    }

    public int timeAddStatic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addStatic( 1, i );
        }
        return r;
    }

    public int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

Y mi propia clase de prueba fue:

public class TestPerformanceOfStaticVsDynamicCalls {

    private static final int RUNS = 1_000_000_000;

    public static void main( String [] args ) throws Exception{

        new TestPerformanceOfStaticVsDynamicCalls().run();
    }

    private void run(){

        int r=0;
        long start, end;

        for( int loop = 0; loop<10; loop++ ){

            // Benchmark

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addStatic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Static: " + ( end - start ) + " ms" );

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addDynamic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Dynamic: " + ( end - start ) + " ms" );

            // Do something with r to keep compiler happy
            System.out.println( r );

        }

    }

    private int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}
Scheintod
fuente
Sería interesante ver los resultados en varios dispositivos y versiones de Android
Blundell
Sí. Pensé que te interesaría :) Pero como estás construyendo software de Android y probablemente tienes un dispositivo Android conectado a tu estación de desarrollo, te sugiero que escojas el código, lo ejecutes y compartas los resultados.
Scheintod
-2

Los métodos que puede declarar como estáticos son los que no requieren instanciación, como

public class MyClass
{
    public static string InvertText(string text)
    {
        return text.Invert();
    }
}

Que luego puede llamar a cambio en cualquier otra clase sin instanciar esa clase.

public class MyClassTwo
{
    public void DoSomething()
    {
        var text = "hello world";
        Console.Write(MyClass.InvertText(text));
    }
}

... Pero eso es algo que probablemente ya sepa. No le brinda ningún beneficio real per se, aparte de dejar más claro que el método no usa ninguna variable de instancia.

En otras palabras, puede apagarlo completamente de manera más segura. Si sabe que nunca usará un método en otras clases (en cuyo caso debería ser privado), no necesita que sea estático en absoluto.

NeroS
fuente
1
¿Cuál es ese idioma? ¿Ese ejemplo se compila? Aquí nada es estático.
Piotr Perak
De hecho, parece que olvidé 'estático' del método InvertText. Y es un ejemplo basado en c #
NeroS