¿Deben los métodos auxiliares privados ser estáticos si pueden ser estáticos?

205

Digamos que tengo una clase diseñada para ser instanciada. Tengo varios métodos privados "auxiliares" dentro de la clase que no requieren acceso a ninguno de los miembros de la clase y operan únicamente con sus argumentos, devolviendo un resultado.

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne(member);
       total += computeMore(member);
       return total;         
   }

   private double computeOne(Something arg) { ... }
   private double computeMore(Something arg) {... } 
} 

¿Hay alguna razón particular para especificar computeOney computeMorecomo métodos estáticos, o alguna razón particular para no hacerlo?

Sin duda, es más fácil dejarlos como no estáticos, aunque ciertamente podrían ser estáticos sin causar ningún problema.

avalys
fuente
3
Vea también cuándo no usar la palabra clave static en Java: stackoverflow.com/questions/1766715/…
Mark Butler

Respuestas:

174

Prefiero que tales métodos de ayuda sean private static; lo que dejará en claro al lector que no modificarán el estado del objeto. Mi IDE también mostrará llamadas a métodos estáticos en cursiva, por lo que sabré que el método es estático sin buscar la firma.

Esko Luontola
fuente
2
Uno de mis hilos favoritos. Estoy usando NetBeans y muestra llamadas a métodos estáticos con fuentes en cursiva.
skiabox
2
Normalmente declaro esos métodos auxiliares como lo protected staticque los hace verificables muy fácilmente en una clase de prueba en el mismo paquete.
James
2
@James o simplemente static(si solo lo queremos para probar).
mrod
3
"No modificará el estado del objeto": explicación perfecta de cómo diferenciarlo de un método privado.
HopeKing
110

Puede resultar en un código de bytes ligeramente más pequeño, ya que los métodos estáticos no tendrán acceso this. No creo que haga ninguna diferencia en la velocidad (y si lo hiciera, probablemente sería demasiado pequeño para hacer una diferencia en general).

Los haría estáticos, ya que generalmente lo hago si es posible. Pero solo soy yo.


EDITAR: Esta respuesta sigue siendo rechazada, posiblemente debido a la afirmación no confirmada sobre el tamaño del código de bytes. Así que realmente haré una prueba.

class TestBytecodeSize {
    private void doSomething(int arg) { }
    private static void doSomethingStatic(int arg) { }
    public static void main(String[] args) {
        // do it twice both ways
        doSomethingStatic(0);
        doSomethingStatic(0);
        TestBytecodeSize t = new TestBytecodeSize();
        t.doSomething(0);
        t.doSomething(0);
    }
}

Bytecode (recuperado con javap -c -private TestBytecodeSize):

Compiled from "TestBytecodeSize.java"
class TestBytecodeSize extends java.lang.Object{
TestBytecodeSize();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

private void doSomething(int);
  Code:
   0:   return

private static void doSomethingStatic(int);
  Code:
   0:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   invokestatic    #2; //Method doSomethingStatic:(I)V
   4:   iconst_0
   5:   invokestatic    #2; //Method doSomethingStatic:(I)V
   8:   new     #3; //class TestBytecodeSize
   11:  dup
   12:  invokespecial   #4; //Method "<init>":()V
   15:  astore_1
   16:  aload_1
   17:  iconst_0
   18:  invokespecial   #5; //Method doSomething:(I)V
   21:  aload_1
   22:  iconst_0
   23:  invokespecial   #5; //Method doSomething:(I)V
   26:  return

}

Invocar el método estático requiere dos bytecodes (byteops?): iconst_0(Para el argumento) y invokestatic.
Invocar el método no estático requiere tres: aload_1(para el TestBytecodeSizeobjeto, supongo), iconst_0(para el argumento) y invokespecial. (Tenga en cuenta que si estos no hubieran sido métodos privados, lo habría sido en invokevirtuallugar de invokespecial; consulte JLS §7.7 Métodos de invocación ).

Ahora, como dije, no espero que haya una gran diferencia en el rendimiento entre estos dos, aparte del hecho de que invokestaticrequiere un código de bytes menos. invokestaticy invokespecialambos deberían ser un poco más rápidos que invokevirtual, ya que ambos usan enlace estático en lugar de dinámico, pero no tengo idea si alguno es más rápido que el otro. Tampoco puedo encontrar buenas referencias. Lo más cercano que puedo encontrar es este artículo de JavaWorld de 1997 , que básicamente repite lo que acabo de decir:

Las instrucciones más rápidos serán muy probablemente invokespecialy invokestatic, porque los métodos invocados por estas instrucciones están ligados de forma estática. Cuando la JVM resuelve la referencia simbólica para estas instrucciones y la reemplaza por una referencia directa, esa referencia directa probablemente incluirá un puntero a los códigos de bytes reales.

Pero muchas cosas han cambiado desde 1997.

En conclusión ... Supongo que todavía me quedo con lo que dije antes. La velocidad no debería ser la razón para elegir uno sobre el otro, ya que sería una micro optimización en el mejor de los casos.

Michael Myers
fuente
Todos estos votos negativos para lo que parece una respuesta muy directa. +1 en interés de la verdad, la justicia, ....
Steve B.
Gracias. (Aunque podría haberlo borrado y obtenido una insignia mientras estaba a -3.: D)
Michael Myers
2
El compilador JIT de todos modos los alineará y optimizará, por lo que la cantidad de instrucciones de bytecode tiene poco que ver con lo rápido que será.
Esko Luontola
3
Es por eso que dije "No creo que haya ninguna diferencia en la velocidad (y si lo hiciera, probablemente sería demasiado pequeño para hacer una diferencia en general)".
Michael Myers
77
Antes de participar en cualquier micro optimización de ese tipo, debe considerar el efecto del compilador de hot spot. En
pocas palabras
18

Mi preferencia personal sería declararlos estáticos, ya que es una clara señal de que no tienen estado.

Steve B.
fuente
18

La respuesta es, depende.

Si miembro es una variable de instancia específica del objeto con el que está tratando, ¿por qué pasarla como parámetro?

Por ejemplo:

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne();
       total += computeMore();
       return total;         
   }

   private double computeOne() { /* Process member here */ }
   private double computeMore() { /* Process member here */ } 
}
Powerlord
fuente
55
+1: Aunque el ejemplo es malo, demasiados métodos que PODRÍAN ser estáticos son malos olores de código. Por cierto, los métodos estáticos son en realidad funciones, no son en absoluto OO. Pueden pertenecer como método en otra clase, o puede faltar una clase a la que esta función podría pertenecer como método.
Bill K
11

Una razón por la que es posible que desee declarar métodos auxiliares estáticos es si necesita llamarlos en el constructor de la clase "antes" thiso super. Por ejemplo:

public class MyClass extends SomeOtherClass { 
    public MyClass(String arg) {
       super(recoverInt(arg));
    }

    private static int recoverInt(String arg) {
       return Integer.parseInt(arg.substring(arg.length() - 1));
    }
}

Este es un ejemplo un poco artificial, pero claramente recoverIntno puede ser un método de instancia en este caso.

oxbow_lakes
fuente
puede llamar a un método de instancia en el constructor, pero si está delegando a otro constructor con super (...) o this (...), no puede llamar a un método de instancia hasta después de la delegación (pero las estadísticas son ok como lo señalas)
Kip
10

Realmente no puedo pensar en ventajas claras para el método estático privado. Dicho esto, tampoco hay ventajas específicas para hacerlos no estáticos. Es principalmente una cuestión de presentación: es posible que desee que sean estáticos para subrayar claramente el hecho de que no están alterando un objeto.

Para el método con diferentes privilegios de acceso, creo que hay dos argumentos principales:

  • Se pueden llamar métodos estáticos sin crear una instancia de un objeto, lo que puede ser útil
  • los métodos estáticos no se pueden heredar, lo que puede ser un problema si necesita polimorfismo (pero es irrelevante para los métodos privados).

Además de eso, la diferencia es bastante pequeña, y dudo mucho que el extra que este puntero pasó al método de instancia haga una diferencia significativa.

Axelle Ziegler
fuente
44
La ventaja de no hacerlo estático es que alguien puede subclasificar y anular el comportamiento del método.
Clint Miller
77
Clint es un método privado, por lo que no puede anular el método.
Bhushan Bhangale
Exactamente, para el método privado, no creo que haga mucha diferencia. Sin embargo, para los métodos públicos, estoy de acuerdo con lo que estás diciendo
Axelle Ziegler,
8

La respuesta correcta es:

cualquier método que no tome ninguna información de un campo, y no ponga ninguna información en un campo, no tiene que ser un método de instancia. Cualquier método que no use o altere ningún campo en su clase u objeto también podría ser un método estático.

Gyan Singh
fuente
5

o alguna razón particular para no [declararlos como estáticos]?

Si.

Al mantenerlos como métodos de instancia, te permites proporcionar una implementación diferente más adelante.

Puede sonar tonto (y en realidad lo sería si esos métodos los usa solo usted en un programa de 50 líneas) pero en aplicaciones más grandes, o en bibliotecas usadas por otra persona, puede decidir elegir una mejor implementación, pero no lo haga. quiere romper el código existente.

Entonces crea una subclase y la devuelve en las nuevas versiones, y dado que los métodos se declararon como métodos de instancia, simplemente deja que el polimorfismo haga su trabajo.

Además, podría beneficiarse al hacer que el constructor sea privado y proporcionar un método de fábrica estático por el mismo motivo.

Por lo tanto, mi recomendación es mantenerlos como métodos de instancia y evitar la estática si es posible.
Aproveche el dinamismo que proporciona el lenguaje.

Vea aquí un video algo relacionado: Cómo diseñar una buena API y por qué es importante

Aunque no está directamente relacionado con la discusión del método "estática vs instancia", toca algunos puntos interesantes en el diseño de API.

OscarRyz
fuente
1
Completamente de acuerdo. Por lo general, trato de evitar la estática y la privacidad por esta misma razón. Creo que la mayoría de las otras respuestas han perdido el punto.
Clint Miller
22
Estamos hablando de ayudantes privados , lo que significa que no son parte de la API pública de la clase. Eso significa que nada le impide proporcionar una implementación diferente más adelante, haciéndolas no estáticas o haciendo otros cambios.
Jonik
Aaaahmm ... ese es un buen punto Jonik. Aún así, crear un buen hábito debería ser razón suficiente (subjetivamente hablando, por supuesto)
OscarRyz
2
Totalmente en desacuerdo. No hay una buena razón por la que desee cambiar un método privado sin cambiar la clase en la que está definido. La única forma de hacer lo que sugiere es mediante la manipulación de código de bytes.
Peter Lawrey
2
Lo que sugiera podría tener sentido si el método no fuera privado, pero incluso entonces sugeriría que diseñe en función de una necesidad actual en lugar de diseñar en función de una necesidad imaginada.
Peter Lawrey
5

Un problema acerca de tener métodos estáticos es que puede hacer que el objeto sea más difícil de usar en pruebas unitarias . Mockito no puede crear simulacros para métodos estáticos y no puede crear una implementación de subclase del método.

Jon Onstott
fuente
2
No deberías escribir pruebas para métodos privados. Ver aquí . No es necesario probar un método privado si es estático o no.
BW
@BW Eso es algo muy subjetivo. Hay muchas razones válidas para probar un método privado.
ruohola
4

Si el método es básicamente solo una subrutina que nunca usará previsiblemente información de estado, declare estática.

Esto permite que se use en otros métodos estáticos o en la inicialización de clases, es decir:

public class Example {
   //...

   //Only possible if computeOne is static
   public final static double COMPUTED_ONE = computeOne(new Something("1"));

   //...
}
Dormir
fuente
3

La pregunta estática / no estática se reduce a "¿realmente necesitaré usar un objeto de esta clase"?

Entonces, ¿estás pasando el objeto entre diferentes métodos? ¿El objeto contiene información útil fuera del contexto de los métodos estáticos? ¿Hay alguna razón para no definir métodos en ambos sentidos si los usará en ambos sentidos?

Si tiene este dilema, me parece que tiene todos los datos necesarios para el método flotando en su código fuera del objeto. ¿Es esto lo que quieres? ¿Sería más fácil recopilar siempre esos datos en un objeto cada vez? Puede ser ambivalente acerca de comprometerse con un solo modelo. Si puede hacerlo todo de una manera, elija estática o no estática y vaya con ella.

notnot
fuente
3

Mi preferencia en casos como estos es hacer computeOney computeMoremétodos estáticos. La razón: encapsulación. Cuanto menos código tenga acceso a la implementación de su clase, mejor.

En el ejemplo que da, declara eso computeOney computeMoreno debería necesitar acceder a las partes internas de la clase, entonces, ¿por qué dar la oportunidad a los encargados de la clase de entrometerse con las partes internas?

maxaposteriori
fuente
3

Me gustaría aclarar algunas cosas que otros carteles han dicho ya que da información incorrecta.

En primer lugar, dado que los métodos son privados, incluso si los declara estáticos, no podrá acceder a ellos fuera de esta clase. En segundo lugar, son privados, por lo que ni siquiera puede anular en la subclase, por lo que estático o no estático no hace ninguna diferencia. En tercer lugar, también se puede llamar a un método privado no estático desde un constructor de la clase, no necesita ser estático.

Ahora viene a su pregunta si un método auxiliar privado debe definirse como estático o no estático. Iré con la respuesta de Steve, ya que marcar un método privado estático muestra que este método no tiene estado ya que también sigo esta regla cuando codifico.

Bhushan Bhangale
fuente
pero hay lugares donde solo se puede llamar a un método estático, como en mi ejemplo y en el ejemplo de Chris Marshall
Kip
Sí, las respuestas de Kip your y Chris son correctas. No he comentado sobre ellos, ya que son respuestas correctas. Solo hablé de respuestas incorrectas.
Bhushan Bhangale
3

Según la experiencia, afirmaría que tales métodos privados tienden a ser bastante universales y reutilizables.

Creo que lo primero que debe hacer es hacer la pregunta de si el método puede ser útil fuera del contexto de clase actual. Si es así, haría exactamente lo que Todos sugieren y extraería este método como estático para algunas clases de utilidades donde alguien espera verificar antes de implementar un nuevo método que haga exactamente lo mismo.

Tales métodos privados de uso general son fuente de gran parte de la duplicación de código en el proyecto porque cada desarrollador los reinventa de forma independiente solo en el lugar donde necesita usarlo. Entonces, la centralización de tales métodos es un camino a seguir.

Piotr Sobczyk
fuente
2

Más específicamente al ejemplo que has dado, parece que el propósito de la definición de estos métodos es más para la claridad del código cuando lo estás leyendo que para la funcionalidad (que se definen como privado). En ese caso, ir con static realmente no hace nada para usted, ya que el propósito de static es exponer la funcionalidad de la clase.

notnot
fuente
Esta pequeña respuesta, enterrada bajo una carga de otras respuestas, realmente toca la esencia del asunto: funcionalidad a nivel de clase versus funcionalidad a nivel de objeto.
eljenso
Gracias, el, realmente lo aprecio. No me importaría votar un poco, tampoco :)
no es
Le di un +1 cuando escribí el comentario.
eljenso
1
Estoy en desacuerdo. La claridad del código sigue siendo importante para los métodos privados. Puede ser menos importante, pero aún es algo por lo que luchar.
LegendLength
1

Una razón es que, siendo todo lo demás igual, las llamadas a métodos estáticos deberían ser más rápidas. Los métodos estáticos no pueden ser virtuales y no toman implícita esta referencia.

dsimcha
fuente
Vea mi respuesta a continuación (agregué el análisis después de que ya estaba en -3, por lo que no es muy visible).
Michael Myers
1

Como mucha gente dijo, ¡hazlo como estático ! Aquí está la regla del pulgar que sigo: si cree que el método es solo una función matemática, es decir, no tiene estado, no involucra ninguna variable de instancia (=> no hay vars de color azul [en eclipse] en el método), y el resultado de el método será el mismo para 'n' número de llamadas (con los mismos parámetros, por supuesto), luego marque ese método como ESTÁTICO.

Y si cree que este método será útil para otra clase, muévalo a una clase Util, de lo contrario, coloque el método como privado en la misma clase. (minimizando la accesibilidad)

jai
fuente
1

Fuera del tema: mantendría los métodos auxiliares en una utilidad independiente / clase auxiliar con solo métodos estáticos.

El problema con tener métodos auxiliares en el punto de uso (léase 'misma clase') es que alguien más adelante puede elegir publicar sus propios métodos auxiliares no relacionados en el mismo lugar

Todo el mundo
fuente
-1: SO no es un foro de mensajería. Lo harás mejor haciendo una pregunta adecuada.
Stu Thompson
1
Prefiero tener los métodos auxiliares junto con la clase a la que están vinculados / relacionados como estáticos. Aún más, puedo exponer algunos como públicos si se deben usar externamente. De esta forma, será más fácil mantener la API para esa clase, si se supone que está expuesta públicamente y se usa intensamente.
Ivaylo Slavov el
1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

La ventaja de los métodos estáticos privados es que pueden reutilizarse más adelante si necesita reinicializar la variable de clase.

mug896
fuente
1
  1. Sin el modificador estático, no puede darse cuenta de que el método no tiene estado sin un análisis adicional que se puede hacer fácilmente cuando (re) escribe el método.

  2. Luego, el modificador "estático" puede darle ideas sobre la refactorización además de otras cosas que otros pueden considerar inútiles. Por ejemplo, mover el método a alguna clase de utilidad o convertirlo a un método miembro.

bodrin
fuente
0

Los declararía como estáticos para marcarlos como apátridas.

Java no tiene un mejor mecanismo para operaciones menores que no se exportan, por lo que creo que la estática privada es aceptable.

Uri
fuente