optimized moz img images for example description alternative java equals

java - moz - ¿Por qué estos== pero no `es igual a()`?



alternative text for images (8)

Estoy un poco confundido acerca de la forma en que Java trata == y equals() cuando se trata de int , Integer y otros tipos de números. Por ejemplo:

Integer X = 9000; int x = 9000; Short Y = 9000; short y = 9000; List<Boolean> results = new ArrayList<Boolean>(); // results.add(X == Y); DOES NOT COMPILE 1) results.add(Y == 9000); // 2) results.add(X == y); // 3) results.add(X.equals(x)); // 4) results.add(X.equals(Y)); // 5) results.add(X.equals(y)); // 6) System.out.println(results);

salidas (tal vez debería hacer su conjetura primero):

[true, true, true, false, false]

  1. Que X == Y no compile es de esperarse, siendo objetos diferentes.
  2. Estoy un poco sorprendido de que Y == 9 sea true , dado que 9 es por defecto un int , y dado que 1) ni siquiera se compiló. Tenga en cuenta que no puede poner un int en un método que espera un Short , pero aquí son iguales.
  3. Esto es sorprendente por la misma razón que dos, pero parece peor.
  4. No es de extrañar, ya que x es autoboxed y Integer .
  5. No es sorprendente, ya que los objetos en diferentes clases no deben ser equal() .
  6. ¿¿Qué?? X == y es true pero X.equals(y) es false ? ¿No debería ser == siempre más estricto que equals() ?

Apreciaría si alguien me puede ayudar a entender esto. ¿Por qué razón se comportan de esta manera == y equals ()?

Edición: He cambiado de 9 a 9000 para mostrar que este comportamiento no está relacionado con ninguna de las formas inusuales en que se comportan los enteros de -128 a 127.

Edición: OK, si crees que entiendes esto, debes considerar lo siguiente, solo para asegurarte:

Integer X = 9000; Integer Z = 9000; short y = 9000; List<Boolean> results = new ArrayList<Boolean>(); results.add(X == Z); // 1) results.add(X == y); // 2) results.add(X.equals(Z)); // 3) results.add(X.equals(y)); // 4) System.out.println(results);

salidas:

[false, true, true, false]

La razón, la mejor como la entiendo:

  1. Diferente ejemplo, tan diferente.
  2. X caja, entonces el mismo valor, por lo tanto igual.
  3. Mismo valor, tan igual.
  4. y no se puede encuadrar en un Integer por lo que no puede ser igual.

(pequeñas) Las instancias de enteros se almacenan en caché, por lo que la invariante x == y se mantiene para las pequeñas instancias (en realidad, -127 +128, depende de JVM):

Integer a = 10; Integer b = 10; assert(a == b); // ok, same instance reused a = 1024; b = 1024; assert(a == b); // fail, not the same instance.... assert(a.equals(b)); // but same _value_

EDITAR

4) y 5) producen falso porque equals tipos de verificación: X es un número entero, mientras que Y es un corto. Este es el método java.lang.Integer#equals :

public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }


Esta conversión automática se llama autoboxing.


Java convertirá un entero en un int automáticamente, si es necesario. Lo mismo se aplica a corto. Esta característica se llama autoboxing y autounboxing. Puedes leer sobre esto here .

Significa que cuando ejecutas el código:

int a = 5; Integer b = a; System.out.println(a == b);

Java lo convierte en:

int a = 5; Integer b = new Integer(a); System.out.println(a == b.valueOf());


La moral de la historia:

Autoboxing / unboxing es confuso, como lo es la promoción de tipo. Juntos, hacen buenos enigmas, pero código horrible.

En la práctica, rara vez tiene sentido usar tipos numéricos más pequeños que int, y estoy casi inclinado a configurar mi compilador de eclipse para marcar todo el autofoto y el no-caja como un error.


La razón por

X == y

Ser verdad tiene que ver con la promoción numérica binaria. Cuando al menos un operando para el operador de igualdad es convertible a un tipo numérico, se usa el operador de igualdad numérico . Primero, el primer operando es sin caja. Entonces, ambos operandos se convierten a int .

Mientras

X.equals(y)

es una llamada de función normal. Como se ha mencionado, y se convertirá automáticamente en un objeto Short . Integer.equals siempre devuelve false si el argumento no es una instancia de Integer . Esto se puede ver fácilmente al inspeccionar la implementación.

Se podría argumentar que este es un defecto de diseño.


Recuerdo que una buena práctica para anular "igual (objeto obj)" es de verificar primero el tipo de parámetro pasado. Por lo tanto, esto hace que X.equals (Y) sea falso . Puede consultar el código de la fuente para descubrir la verdad :)


Su problema aquí no es solo cómo se trata == sino también el autoboxing ... Cuando compara Y y 9, está comparando dos primitivas que son iguales, en los últimos dos casos obtiene falso simplemente porque así es como funcionan los iguales. Dos objetos son iguales solo si son del mismo tipo y tienen el mismo valor. Cuando dices en "X.equals (y)" le estás diciendo que haga Integer.equals (Corto) y al observar la implementación de Integer.equals () fallará:

public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }

Debido al autoboxing, los dos últimos darán como resultado el mismo fallo, ya que ambos pasarán como Shorts.

Edición: Olvidé una cosa ... En el caso de results.add (X == y); Desempaquetará X y hará (X.intValue () == y) que resulta ser cierto, así como 9 == 9


Un poco más de detalles sobre cómo funciona el autoboxing y cómo se almacenan en caché los objetos Enteros con valores "pequeños":

Cuando un int primitivo se convierte en un bus automático en un entero, el compilador lo hace reemplazando el código con una llamada a Integer.valueOf (...). Por lo tanto, lo siguiente:

Integer a = 10;

Se sustituye por el compilador con lo siguiente:

Integer a = Integer.valueOf(10);

El método valueOf (...) de la clase Integer mantiene un caché que contiene objetos Integer para todos los valores entre -127 y 128. Si llama a valueOf (...) con un valor que está en este rango, el método devuelve una Objeto existente desde el caché. Si el valor está fuera del rango, devuelve un nuevo objeto Integer inicializado con el valor especificado. (Si desea saber exactamente cómo funciona, busque el archivo src.zip en el directorio de instalación de JDK y busque el código fuente de la clase java.lang.Integer).

Ahora, si haces esto:

Integer a = 10; Integer b = 10; System.out.println(a == b);

verá que se imprime true , pero no porque a y b tengan el mismo valor, sino porque a y b hacen referencia al mismo objeto Integer, el objeto de la caché que devuelve Integer.valueOf (...).

Si cambias los valores:

Integer a = 200; Integer b = 200; System.out.println(a == b);

luego se imprime falso , porque 200 está fuera del rango del caché, por lo que a y b se refieren a dos objetos enteros distintos.

Es desafortunado que == se use para la igualdad de objetos para tipos de valor como las clases de envoltura y String en Java; es contrario a la intuición.