¿Cuál es un buen caso de uso para la importación estática de métodos?

Acabo de recibir un comentario de revisión de que mi importación estática del método no era una buena idea. La importación estática era de un método de una clase DA, que tiene principalmente métodos estáticos. Entonces, en medio de la lógica comercial, tenía una actividad DA que aparentemente parecía pertenecer a la clase actual:

import static some.package.DA.*; class BusinessObject { void someMethod() { .... save(this); } } 

Al crítico no le gustó cambiar el código y yo no, pero sí estoy de acuerdo con él. Una de las razones dadas para la importación no estática era que confundía dónde se definía el método, no estaba en la clase actual ni en ninguna superclase, por lo que también le quedaba tiempo para identificar su definición (el sistema de revisión basado en web no tiene clic) enlaces como IDE 🙂 Realmente no creo que esto importe, la estática: las importaciones todavía son bastante nuevas y pronto todos nos acostumbraremos a localizarlas.

Pero la otra razón, con la que estoy de acuerdo, es que una llamada a método no calificado parece pertenecer al objeto actual y no debe saltar contextos. Pero si realmente perteneciera, tendría sentido extender esa súper clase.

Entonces, ¿cuándo tiene sentido para los métodos de importación estáticos? ¿Cuándo lo has hecho? ¿Te gustó / te gusta la forma en que se ven las llamadas no calificadas?

EDITAR: La opinión popular parece ser que los métodos de importación estática si nadie va a confundirlos como métodos de la clase actual. Por ejemplo, métodos de java.lang.Math y java.awt.Color. Pero si abs y getAlpha no son ambiguos, no veo por qué readEmployee. Como en muchas opciones de progtwigción, creo que esto también es una preferencia personal.

Gracias por su respuesta chicos, estoy cerrando la pregunta.

Esto es de la guía de Sun cuando lanzaron la función (énfasis en el original):

Entonces, ¿cuándo deberías usar la importación estática? Muy escasamente! Úselo únicamente cuando esté tentado de declarar copias locales de las constantes o abusar de la herencia (Constant Constant Antipattern). … Si utiliza en exceso la característica de importación estática, puede hacer que su progtwig sea ilegible y no se pueda mantener, contaminando su espacio de nombre con todos los miembros estáticos que importa. Los lectores de su código (incluido usted, algunos meses después de haberlo escrito) no sabrán de qué clase proviene un miembro estático. La importación de todos los miembros estáticos de una clase puede ser particularmente perjudicial para la legibilidad; si solo necesita uno o dos miembros, impórtelos individualmente.

( https://docs.oracle.com/javase/8/docs/technotes/guides/language/static-import.html )

Hay dos partes que quiero llamar específicamente:

  • Use importaciones estáticas solo cuando esté tentado a “abusar de la herencia”. En este caso, ¿habría estado tentado de que BusinessObject extend some.package.DA ? Si es así, las importaciones estáticas pueden ser una forma más limpia de manejar esto. Si nunca hubieras soñado con extender some.package.DA , entonces este es probablemente un mal uso de las importaciones estáticas. No lo use solo para guardar unos pocos caracteres al escribir.
  • Importar miembros individuales. Digamos que import static some.package.DA.save lugar de DA.* . Eso hará que sea mucho más fácil encontrar de dónde viene este método importado.

Personalmente, he utilizado esta función de idioma muy raramente, y casi siempre solo con constantes o enumeraciones, nunca con métodos. El intercambio, para mí, casi nunca vale la pena.

Otro uso razonable para las importaciones estáticas es con JUnit 4. En versiones anteriores de los métodos JUnit como assertEquals y fail se heredaban ya que la clase de prueba extendía junit.framework.TestCase .

 // old way import junit.framework.TestCase; public class MyTestClass extends TestCase { public void myMethodTest() { assertEquals("foo", "bar"); } } 

En JUnit 4, las clases de prueba ya no necesitan extender TestCase y pueden usar anotaciones. A continuación, puede importar de forma estática los métodos assert de org.junit.Assert :

 // new way import static org.junit.Assert.assertEquals; public class MyTestClass { @Test public void myMethodTest() { assertEquals("foo", "bar"); // instead of Assert.assertEquals("foo", "bar"); } } 

JUnit documentos usándolo de esta manera.

Effective Java, Second Edition , al final del Elemento 19, señala que puede usar importaciones estáticas si se encuentra utilizando en gran medida constantes de una clase de utilidad. Creo que este principio se aplicaría a las importaciones estáticas de constantes y métodos.

 import static com.example.UtilityClassWithFrequentlyUsedMethods.myMethod; public class MyClass { public void doSomething() { int foo= UtilityClassWithFrequentlyUsedMethods.myMethod(); // can be written less verbosely as int bar = myMethod(); } } 

Tiene sus ventajas y desventajas. Hace que el código sea un poco más legible a expensas de perder alguna información inmediata sobre dónde se define el método. Sin embargo, un buen IDE te permitirá ir a la definición, así que esto no es un gran problema.

Debería seguir utilizándolo con moderación, y solo si se encuentra utilizando muchas cosas del archivo importado muchas, muchas veces.

Editar: se actualizó para ser más específico a los métodos, ya que es a lo que se refiere esta pregunta. El principio se aplica independientemente de lo que se importe (constantes o métodos).

Lo uso mucho para Color.

 static import java.awt.Color.*; 

Es muy poco probable que los colores se confundan con otra cosa.

Estoy de acuerdo en que pueden ser problemáticos desde una perspectiva de legibilidad y deben usarse con moderación. Pero al usar un método estático común, en realidad pueden boost la legibilidad. Por ejemplo, en una clase de prueba JUnit, métodos como assertEquals son obvios de dónde vienen. Del mismo modo para los métodos de java.lang.Math .

Creo que la importación estática es realmente útil para eliminar nombres de clases redundantes cuando se utilizan clases utils como Arrays y Assertions .

No estoy seguro de por qué, pero Ross omitió la última frase que menciona esto en la documentación a la que hace referencia .

Si se usa apropiadamente, la importación estática puede hacer que su progtwig sea más legible, eliminando la repetición repetitiva de nombres de clase.

Básicamente copiado de este blog: https://medium.com/alphadev-thoughts/static-imports-are-great-but-underused-e805ba9b279f

Así por ejemplo:

Afirmaciones en las pruebas

Este es el caso más obvio en el que creo que todos estamos de acuerdo

 Assertions.assertThat(1).isEqualTo(2); // Use static import instead assertThat(1).isEqualTo(2); 

Clases de Utils y enumeraciones

El nombre de clase se puede eliminar en muchos casos cuando se utilizan clases de utilidad que hacen que el código sea más fácil de leer

 List numbers = Arrays.asList(1, 2, 3); // asList method name is enough information List numbers = asList(1, 2, 3); 

El paquete java.time tiene algunos casos donde debería ser usado

 // Get next Friday from now, quite annoying to read LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY)); // More concise and easier to read LocalDate.now().with(next(FRIDAY)); 

Ejemplo de cuándo no usar

 // Ok this is an Optional Optional.of("hello world"); // I have no idea what this is of("hello world"); 

Las importaciones estáticas son la única característica “nueva” de Java que nunca he usado y que no pretendo usar alguna vez, debido a los problemas que acabas de mencionar.

Recomiendo el uso de importación estática cuando se usa OpenGL con Java, que es un caso de uso que cae en la categoría “uso intensivo de constantes de una clase de utilidad”

Considere eso

 import static android.opengl.GLES20.*; 

le permite exportar el código C original y escribir algo legible, como:

 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glUniform1i(samplerUniform, 0); glBindBuffer(GL_ARRAY_BUFFER, vtxBuffer); glVertexAttribPointer(vtxAttrib, 3, GL_FLOAT, false, 0, 0); 

en lugar de esa fealdad común extendida:

 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); GLES20.glUniform1i(samplerUniform, 0); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vtxBuffer); GLES20.glVertexAttribPointer(vtxAttrib, 3, GLES20.GL_FLOAT, false, 0, 0); 

Son útiles para reducir la verborrea, especialmente en los casos en que se convocan muchos métodos importados, y la distinción entre los métodos locales y los importados es clara.

Un ejemplo: código que implica múltiples referencias a java.lang.Math

Otra: una clase de constructor XML donde anteponer el nombre de clase a cada referencia ocultaría la estructura que se está construyendo

Creo que las importaciones estáticas son ordenadas para NLS en el estilo gettext.

 import static mypackage.TranslatorUtil._; //... System.out.println(_("Hello world.")); 

Esto marca la cadena como una cadena que debe extraerse y proporciona una manera fácil y limpia de reemplazar la cadena con su traducción.

La importación estática IMO es una buena característica. Es absolutamente cierto que la gran dependencia de la importación estática hace que el código sea ilegible y difícil de entender a qué clase pertenece un método o atributo estático. Sin embargo, en mi experiencia se convierte en una característica utilizable, especialmente cuando se diseñan clases de Util que proporcionan algunos métodos y atributos estáticos. La ambigüedad que surge cada vez que se proporciona una importación estática puede eludirse mediante el establecimiento de estándares de código. En mi experiencia dentro de una empresa, este enfoque es aceptable y hace que el código sea más limpio y fácil de entender. Preferiblemente inserto el carácter _ en los métodos estáticos frontales y los atributos estáticos (de alguna manera adoptados de C) . Aparentemente, este enfoque infringe los estándares de nomenclatura de Java pero proporciona claridad al código. Por ejemplo, si tenemos una clase AngleUtils:

 public class AngleUtils { public static final float _ZERO = 0.0f; public static final float _PI = 3.14f; public static float _angleDiff(float angle1, float angle2){ } public static float _addAngle(float target, float dest){ } } 

En este caso, la importación estática proporciona claridad y la estructura del código me parece más elegante:

 import static AngleUtils.*; public class TestClass{ public void testAngles(){ float initialAngle = _ZERO; float angle1, angle2; _addAngle(angle1, angle2); } } 

De inmediato alguien puede decir qué método o atributo proviene de una importación estática y oculta la información de la clase a la que pertenece. No sugiero usar la importación estática para las clases que son parte integral de un módulo y proporcionar métodos estáticos y no estáticos, ya que en estos casos es importante saber qué clase proporciona cierta funcionalidad estática.

Utilizo ‘import static java.lang.Math. *’ Cuando transfiero el código matemático pesado de C / C ++ a java. Los métodos matemáticos asignan 1 a 1 y hacen que difiera el código portado más fácilmente sin la calificación del nombre de clase.

Necesitas usarlos cuando:

  • desea utilizar una instrucción switch con valores enum
  • desea hacer que su código sea difícil de entender

Hablando de pruebas unitarias: la mayoría de las personas usa importaciones estáticas para los diversos métodos estáticos que proporcionan los marcos de burla , como when() o verify() .

 import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; 

Y, por supuesto, cuando use la única afirmación que debe usar `assertThat (), resulta útil para importar estáticamente los matccrest matchers requeridos, como en:

 import static org.hamcrest.Matchers.*; 

Los uso cuando puedo. Tengo la configuración IntelliJ para recordarme si me olvido. Creo que se ve mucho más limpio que un nombre de paquete completamente calificado.