Importaciones estáticas de Java
scope language-design (4)
No creo que sea un error o algo diferente de la importación normal. Por ejemplo, en el caso de una importación normal, si tiene una clase privada con el mismo nombre que la importada, la importada no se reflejará.
Solo por experimento, descubrí que los métodos no estáticos de Java anulan todos los mismos métodos nombrados en el alcance, incluso en contexto estático. Incluso sin permitir la sobrecarga de parámetros. Me gusta
import java.util.Arrays;
import static java.util.Arrays.toString;
public class A {
public static void bar(Object... args) {
Arrays.toString(args);
toString(args); //toString() in java.lang.Object cannot be applied to (java.lang.Object[])
}
}
No puedo encontrar nada sobre esto en la especificación. ¿Es esto un error? Si no es así, ¿hay alguna razón para implementar un lenguaje como ese?
UPD: Java 6 no compila este ejemplo. La pregunta es, ¿por qué?
No es una anulación. Si funcionó, this.toString()
seguiría accediendo al método de A
lugar de Arrays.toString
como sería el caso si se hubiera producido la anulación.
La especificación del lenguaje explica que las importaciones estáticas solo afectan la resolución de métodos y tipos static
:
Una declaración d de importación estática única en una unidad de compilación c del paquete p que importa un campo llamado n sombrea la declaración de cualquier campo estático llamado n importado por una declaración de importación estática a demanda en c, en todo c.
Una declaración d de importación estática única en una unidad de compilación c del paquete p que importa un método llamado n con signature s shadows la declaración de cualquier método estático llamado n con la firma s importada por una declaración static-import-on-demand en c , a lo largo de c.
Una declaración d de importación estática única en una unidad de compilación c del paquete p que importa un tipo llamado n sombrea las declaraciones de:
- cualquier tipo estático llamado n importado por una declaración static-import-on-demand en c.
- cualquier tipo de nivel superior (§7.6) nombrado n declarado en otra unidad de compilación (§7.3) de p.
- cualquier tipo llamado n importado por una declaración tipo-importación-a-demanda (§7.5.2) en c. a lo largo de c.
Las importaciones estáticas no sombrean los métodos no estáticos o los tipos internos.
Entonces toString
no toString
el método no estático. Como el nombre toString
puede referirse a un método no estático de A
, no puede hacer referencia al método static
de Arrays
y, por lo tanto, toString
une al único método llamado toString
disponible en el alcance, que es String toString()
. Ese método no puede tomar ningún argumento para que obtenga un error de compilación.
La Sección 15.12.1 explica la resolución del método y debería haber sido completamente reescrita para permitir el sombreado de nombres de métodos no disponibles dentro de métodos static
pero no dentro de member
métodos del member
.
Supongo que los diseñadores de idiomas querían mantener simples las reglas de resolución de métodos, lo que significa que el mismo nombre significa lo mismo, ya sea que aparezca en un método static
o no, y lo único que cambia es cuáles están disponibles.
Si intenta seguir un código de aspecto similar , entonces no obtendrá ningún error de compilación
import static java.util.Arrays.sort;
public class StaticImport {
public void bar(int... args) {
sort(args); // will call Array.sort
}
}
El motivo por el que compila y el suyo no es porque el toString()
(o cualquier otro método definido en la clase Objeto) todavía tiene un ámbito en la clase Object debido a que Object es el padre de su clase. Por lo tanto, cuando el compilador encuentra la firma correspondiente de esos métodos de la clase Object, genera un error de compilación. En mi ejemplo, dado que la clase Object no tiene el método sort(int[])
, el compilador lo relaciona con la importación estática .
La explicación es simple, aunque no cambia el hecho de que el comportamiento es muy poco intuitivo:
Al resolver el método que se va a invocar, lo primero que hace el compilador es encontrar el ámbito de delimitación más pequeño que tenga un método del nombre correcto. Solo entonces vienen otras cosas como resolución de sobrecarga y co en el juego.
Ahora, lo que está sucediendo aquí es que el ámbito adjunto más pequeño que contiene un método toString()
es la clase A que lo hereda de Object
. Por lo tanto, nos detenemos allí y no buscamos más. Tristemente, el próximo compilador intenta encontrar el mejor ajuste de los métodos en el alcance dado y nota que no puede llamar a ninguno de ellos y da un error.
Lo que significa que nunca se deben importar métodos con un nombre idéntico a un método en Object, porque los métodos que tienen un alcance natural tienen prioridad sobre las importaciones estáticas (el JLS describe el sombreado del método en detalle, pero para este problema creo que es mucho más simple recuerda eso).
Editar: @alf envió amablemente la parte correcta de JLS que describe la invocación al método para aquellos que quieren la imagen completa. Es bastante complejo, pero el problema tampoco es simple, así que eso es de esperar.