java static import compiler-errors equals

¿Por qué no puedo "importar estáticamente" un método "igual a" en Java?



static compiler-errors (8)

Me gusta usar este método aquí:

org.apache.commons.lang.ObjectUtils.equals(Object object1, Object object2)

El único inconveniente (en comparación con Google Guava, por ejemplo), es que no puedo importar el método de forma estática. Es decir, esto es inútil:

import static org.apache.commons.lang.ObjectUtils.equals;

... ya que mi compilador Eclipse no enlazará correctamente ese método al escribir

equals(obj1, obj2);

El error es:

El método es igual a (Objeto) en el tipo Objeto no es aplicable para los argumentos (..., ...)

¿Porqué es eso? ¿Mi método importado estáticamente no es aplicable si hay un método con el mismo nombre (pero no la misma firma) en alguno de los supertipos? ¿Se especifica esto formalmente en el JLS? ¿O algún problema del compilador de Eclipse?

ACTUALIZAR

Esto tampoco funciona:

import static org.apache.commons.lang.ObjectUtils.defaultIfNull; public class Test { void test() { defaultIfNull(null, null); // ^^ compilation error here } void defaultIfNull() { } }

javac mensaje de error:

Test.java:5: defaultIfNull() in Test cannot be applied to (<nulltype>,<nulltype>) defaultIfNull(null, null); ^ 1 error


En realidad creo que esto es más un problema de Eclipse que cualquier otra cosa. Si está utilizando una versión sobrecargada de equals () que recibe dos argumentos, no debe haber colisión con el Object.equals () predeterminado.

Hay un par de trucos en Eclipse que puedes usar para que reconozca la importación estática:

1 - Agregar el tipo estático a Organizar Importaciones Ir a:

Window > Preferences > Java > Code Style > Organize Imports

luego haga clic en "Nueva estática", luego "Tipos", luego elija su clase (en este caso, org.apache.commons.lang.ObjectUtils)

Mientras esté en el panel Organizar importaciones, anule la selección de

"Do not create imports for types starting with lowercase letter"

(no olvides esto, es importante)

2 - Agregar el tipo a Asistencia de contenido Ir a:

Window > Preferences > Java > Editor > Content Assist Favorites

luego haga clic en "Nuevo tipo", luego elija su clase (en este caso, nuevamente, org.apache.commons.lang.ObjectUtils)

Ahora con esto deberías poder Ctrl + Espacio en cualquier parte de tu método y obtener el método "igual a (Objeto, Objeto)" como posible contenido. Si elige ese método, Eclipse debería insertar automáticamente la importación estática para iguales.


Es una colisión de métodos con java.awt, necesita hacer referencia al paquete de esta manera:

ObjectUtils.equals(a, b);


Esto no es realmente una respuesta (solo más preguntas de una manera). Esto es una prueba de que el compilador importa los métodos con firma.

package test; public class Foo { public static void equal(Object o1) { System.out.println("Foo.equal Object"); } public static void equal(Integer o1) { System.out.println("Foo.equal Integer"); } } package test; public class Bar { public static void equal(Number o1) { System.out.println("Bar.equal Number"); } } import static test.Foo.equal; import static test.Bar.equal; public static void main(String args[]) throws Exception { equal((Object)null); equal((Number)null); equal((Integer)null); } Output: Foo.equal Object Bar.equal Number Foo.equal Integer

Esto también puede estar relacionado. Un método en una clase interna que "oculta" un método estático en la clase externa con una firma diferente.

http://ideone.com/pWUf1

Parece que el compilador tiene diferentes lugares donde buscar métodos y los comprueba uno por uno, pero solo busca por nombre lo que lleva a una terminación prematura de la búsqueda.


Hice algunas pruebas. Lo primero que noté es que solo necesita una declaración de importación estática para múltiples métodos del mismo nombre.

public class EqualsClass { public static boolean equals(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } public static boolean equals(Object o1, Object o2, Object o3) { return equals(o1, o2) && equals(o2, o3); } } import static mypackage.EqualsClass.equals; public class TestClass { public static void main() { Object o1 = new Object(); Object o2 = new Object(); equals(o1, o2); // Compiles - static context Object o3 = new Object(); equals(o1, o2, o3); // No extra static import required }

Entonces me di cuenta de que no funciona en un contexto de instancia:

public void someInstanceMethod() { Object o1 = new Object(); Object o2 = new Object(); equals(o1, o2); // Does not compile - instance context Object o3 = new Object(); equals(o1, o2, o3); // As expected does not compile } }

Pero luego, si rechazo la importación estática con el propio método estático de la clase:

public static boolean equals(Object o1, Object o2) { return EqualsClass.equals(o1, o2); // Compiles } public void someInstanceMethod() { equals(new Object(), new Object()); // Compiles!! equals(new Object(), new Object(), new Object()); // Doesn''t compile! }

El hecho de que funcione en un contexto estático tiene un sentido razonable para mí. Sin embargo, parece que hay una diferencia significativa entre la resolución de un método importado estáticamente y un método estático definido de la clase.

Resumen:

  • No se puede acceder a los métodos con el mismo nombre que un método de instancia cuando se importan de forma estática desde un contexto de instancia.
  • Se puede acceder a los métodos estáticos de la misma clase con el mismo nombre desde un contexto de instancia.
  • Una importación estática le da acceso a todos los métodos estáticos con el mismo nombre de esa clase a pesar de la firma (parámetros y valor de retorno).

Me interesaría ver la parte del JLS o una especificación del compilador que especifique la resolución de las importaciones estáticas por parte del compilador y cómo son eliminadas por métodos locales.


La colisión es en realidad con Object.equals() . Todas las clases se heredan de Object y, por lo tanto, tienen el método Object.equals() que conduce a esta colisión.

Estás importando por nombre, no por firma. En realidad, no se puede importar un método estático llamado equals debido a esto. O más bien, puede importarlo, pero no usarlo. Estoy de acuerdo en que esto debería funcionar sin embargo.

(Hice mis comentarios mi propia respuesta.)


Según la especificación del lenguaje Java

  1. Si una declaración de importación estática única importa un miembro cuyo nombre simple es n, y la unidad de compilación también contiene una declaración de importación de tipo único que importa un tipo cuyo nombre simple es n, se produce un error en tiempo de compilación. (Este error ocurre incluso si ambas declaraciones se refieren al mismo tipo, debido a que es confuso usar dos mecanismos diferentes para importar redundantemente el mismo tipo).
  2. Si una declaración de importación estática única importa un miembro cuyo nombre simple es n, y la unidad de compilación también declara un tipo de nivel superior cuyo nombre simple es n, se produce un error en tiempo de compilación.

Entonces, en su caso, el punto 2 mencionado anteriormente es la razón por la que está obteniendo un error de tiempo de compilación. así que incluso si las firmas de los métodos son diferentes si los nombres son los mismos, es un error de tiempo de compilación.

Importación estática JSR y JLS


También revisé JLS3 y no pude encontrar una respuesta definitiva.

Según 15.12.1, primero debemos determinar la clase única en la que se declara / hereda el método equals . Aquí tenemos dos clases candidatas, y la especificación no parece tener una regla para resolver el conflicto.

Podemos investigar un problema comparable. Un nombre de tipo simple puede referirse tanto a un tipo importado como a un tipo heredado (un tipo de miembro de la superclase). Javac elige este último. Esto se debe probablemente al procedimiento en 6.5.2, que otorga a las importaciones la prioridad más baja.

Si se aplica el mismo principio, los ObjectUtils.equals importados deberían ceder a Object.equals heredados. Luego, según 15.12.2.1, no hay un método equals en el Object que sea potencialmente aplicable a la expresión equals(obj1, obj2)

Personalmente, preferiría que la importación tenga prioridad sobre la herencia, porque la importación está más cerca. También estabiliza el significado de un nombre. En el esquema actual, suponga que el Object no tiene un método equals , la expresión equals(obj1, obj2) refiere a ObjectUtils.equals ; ahora supongamos que Object agrega el método equals , un movimiento totalmente inocente, de repente la subclase no se compila. Un escenario aún peor: el nuevo método de equals tiene una firma compatible; La subclase aún compila, pero el significado de la expresión cambia silenciosamente.


JLS 15.12.1 . identifica dos razones por las que un método puede estar "dentro del alcance":

  1. "... hay una declaración de tipo adjunto de la que ese método es miembro"
  2. "... debido a una o más importaciones estáticas simples ..."

Ahora dos factores contribuyen al sorprendente resultado:

  1. En este punto solo se considera el nombre del método, las firmas vienen más tarde.
  2. Las dos alternativas mencionadas anteriormente están conectadas con "de lo contrario". En el primer caso, terminamos buscando en la clase adjunta del método visible. En el segundo caso utilizamos la importación estática.

Este "de lo contrario" implica que el alcance de la búsqueda está restringido a solo probar cualquiera de las dos ramas. Primero, tenemos que decidir si buscamos un tipo cerrado o usando una importación estática. El tipo adjunto tiene una prioridad más alta, encontramos un método con el nombre correcto (Test.defaultIfNull ()), la búsqueda termina aquí. Cuando más tarde encontremos que este método es incompatible, no se puede volver a intentar la importación estática.

La situación no es infrecuente en JLS, también otros problemas de búsqueda de métodos se organizan en fases, donde una coincidencia parcial en una fase puede evitar encontrar una mejor coincidencia en una fase posterior. La coincidencia entre aridades fijas y aridades variables es otro ejemplo de este concepto. El efecto en todos los casos es que los compiladores no buscan todo el espacio de solución posible, pero después de que se hayan tomado ciertas decisiones, se cortan y no se visitan las sucursales completas.

Una regla de oro se puede derivar de lo anterior: la sobrecarga solo puede seleccionar entre los métodos de la misma jerarquía de tipos, no puede seleccionar entre métodos de tipos no relacionados por herencia.