¿Cuándo NO utilizar la palabra clave estática en Java?

¿Cuándo se considera una mala práctica utilizar la palabra clave estática en Java en las firmas de métodos? Si un método realiza una función basada en algunos argumentos, y no requiere acceso a campos que no son estáticos, ¿no querría siempre que estos tipos de métodos fueran estáticos?

Una de las razones por las que no desea que sea estática es permitir que se anule en una subclase. En otras palabras, el comportamiento puede no depender de los datos dentro del objeto, sino del tipo exacto del objeto. Por ejemplo, podría tener un tipo de colección general, con una propiedad isReadOnly que devolvería false en colecciones siempre mutables, true en colecciones siempre inmutables y dependería de variables de instancia en otras.

Sin embargo, esto es bastante raro en mi experiencia, y generalmente debería especificarse explícitamente para mayor claridad. Normalmente haré un método que no dependa de ningún estado de objeto estático.

Dos de los males más grandes que encontrarás en aplicaciones Java a gran escala son

  • Métodos estáticos, excepto aquellos que son funciones puras *
  • Campos estáticos mutables

Estos arruinan la modularidad, extensibilidad y capacidad de prueba de su código en un grado que me doy cuenta de que no puedo esperar convencerlo en este tiempo y espacio limitados.

* Una “función pura” es cualquier método que no modifique ningún estado y cuyo resultado no dependa más que de los parámetros que se le proporcionan. Por lo tanto, por ejemplo, cualquier función que realice E / S (directa o indirectamente) no es una función pura, pero Math.sqrt (), por supuesto, sí lo es.

Más blahblah sobre funciones puras (autovínculo) y por qué quieres apegarte a ellas.

Le recomiendo encarecidamente que favorezca el estilo de progtwigción de la “dependency injection”, posiblemente respaldado por un marco como Spring o Guice (descargo de responsabilidad: soy coautor de este último). Si haces esto bien, esencialmente nunca necesitarás estado estático mutable ni métodos estáticos no puros.

En general, prefiero los métodos de instancia por los siguientes motivos:

  1. los métodos estáticos dificultan las pruebas porque no se pueden reemplazar,
  2. los métodos estáticos están más orientados al procedimiento.

En mi opinión, los métodos estáticos están bien para las clases de utilidad (como StringUtils ) pero prefiero evitar usarlos tanto como sea posible.

Lo que dices es cierto, pero ¿qué sucede cuando quieres anular el comportamiento de ese método en una clase derivada? Si es estático, no puedes hacer eso.

Como ejemplo, considere la siguiente clase de tipo DAO:

 class CustomerDAO { public void CreateCustomer( Connection dbConn, Customer c ) { // Some implementation, created a prepared statement, inserts the customer record. } public Customer GetCustomerByID( Connection dbConn, int customerId ) { // Implementation } } 

Ahora, ninguno de esos métodos requiere ningún “estado”. Todo lo que necesitan se pasa como parámetros. Entonces PODRÍAN fácilmente ser estáticos. Ahora viene el requisito de que necesita soportar una base de datos diferente (digamos Oracle)

Como esos métodos no son estáticos, puede crear una nueva clase DAO:

 class OracleCustomerDAO : CustomerDAO { public void CreateCustomer( Connection dbConn, Customer c ) { // Oracle specific implementation here. } public Customer GetCustomerByID( Connection dbConn, int customerId ) { // Oracle specific implementation here. } } 

Esta nueva clase ahora podría usarse en lugar de la anterior. Si está utilizando la dependency injection, es posible que ni siquiera requiera un cambio de código.

Pero si hubiéramos hecho esos métodos estáticos, eso haría las cosas mucho más complicadas ya que no podemos simplemente anular los métodos estáticos en una nueva clase.

Los métodos estáticos generalmente se escriben con dos propósitos. El primer propósito es tener algún tipo de método de utilidad global, similar al tipo de funcionalidad que se encuentra en java.util.Collections . Estos métodos estáticos son generalmente inofensivos. El segundo objective es controlar la instanciación de objetos y limitar el acceso a los recursos (como las conexiones de bases de datos) a través de diversos patrones de diseño, como los singleton y las fábricas . Estos pueden, si se implementan mal, generar problemas.

Para mí, hay dos desventajas de usar métodos estáticos:

  1. Hacen que el código sea menos modular y más difícil de probar / extender. La mayoría de las respuestas ya abordaron esto, así que no entraré en eso más.
  2. Los métodos estáticos tienden a dar lugar a algún tipo de estado global, que a menudo es la causa de errores insidiosos. Esto puede ocurrir en un código mal escrito que está escrito para el segundo propósito descrito anteriormente. Déjame elaborar.

Por ejemplo, considere un proyecto que requiere registrar ciertos eventos en una base de datos, y también depende de la conexión de la base de datos para otro estado. Supongamos que normalmente, la conexión de la base de datos se inicializa primero, y luego el marco de trabajo de registro se configura para escribir ciertos eventos de registro en la base de datos. Ahora suponga que los desarrolladores deciden pasar de un marco de base de datos escrito a mano a un marco de base de datos existente, como hibernación.

Sin embargo, es probable que este marco tenga su propia configuración de registro, y si usa el mismo marco de trabajo de registro que el suyo, entonces existe una buena posibilidad de que haya varios conflictos entre las configuraciones. De repente, cambiar a un marco de base de datos diferente produce errores y fallas en diferentes partes del sistema que aparentemente no están relacionadas. El motivo por el cual pueden ocurrir estas fallas es porque la configuración de registro mantiene el estado global al que se accede a través de métodos y variables estáticos, y varias partes del sistema pueden anular varias propiedades de configuración.

Para alejarse de estos problemas, los desarrolladores deben evitar almacenar cualquier estado a través de métodos y variables estáticos. En su lugar, deben construir API limpias que permitan a los usuarios administrar y aislar el estado según sea necesario. BerkeleyDB es un buen ejemplo aquí, que encapsula el estado a través de un objeto de entorno en lugar de a través de llamadas estáticas.

Está bien. De hecho, debe contorsionar lo que de otra manera sería un diseño razonable (para tener algunas funciones no asociadas con una clase) en términos de Java. Es por eso que ves clases catch-all como FredsSwingUtils y YetAnotherIOUtils.

cuando desee utilizar un miembro de clase independientemente de cualquier objeto de esa clase, debe declararse como estático.
Si se declara estático, se puede acceder sin una instancia existente de un objeto de la clase. Un miembro estático es compartido por todos los objetos de esa clase específica.

Una molestia adicional sobre los métodos estáticos: no hay una manera fácil de pasar una referencia a dicha función sin crear una clase contenedora a su alrededor. Por ejemplo, algo como:

 FunctorInterface f = new FunctorInterface() { public int calc( int x) { return MyClass.calc( x); } }; 

Odio este tipo de make-work de Java. ¿Tal vez una versión posterior de java obtendrá delegates o un mecanismo similar de puntero de función / tipo de procedimiento?

Una pequeña queja, pero una cosa más que no gusta de funciones estáticas gratuitas, er, métodos.

Dos preguntas aquí 1) ¿Un método estático que crea objetos permanece cargado en la memoria cuando se accede por primera vez? ¿No es esto (quedando cargado en la memoria) un inconveniente? 2) Una de las ventajas de usar Java es su característica de recolección de basura. ¿No ignoramos esto cuando utilizamos métodos estáticos?