quarta-feira, 7 de maio de 2014

Jeito certo de comparar Integers em Java

class A {
 public static void main(String[] args){
     Integer a1 = 125;
     Integer b1 = 125;
     System.out.println(a1==b1); //true
 }
}

class B {
 public static void main(String[] args){
  Integer a2 = 280;
  Integer b2 = 280;
  System.out.println(a2==b2);//false
 }
}

class C {
 public static void main(String[] args){
  Integer a3 = 280;
  System.out.println(a3==280);//true
 }
}


Quem já estudou para certificação está acostumado com esse tipo de pegadinha. Para entender esse comportamento vamos analisar o bytecode da classe A e B:

class A extends java.lang.Object{
A();
  Code:
   0: aload_0
   1: invokespecial #1; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
  Code:
   0: bipush 125
   2: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   5: astore_1
   6: bipush 125
   8: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   11: astore_2
   12: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   15: aload_1
   16: aload_2
   17: if_acmpne 24
   20: iconst_1
   21: goto 25
   24: iconst_0
   25: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V
   28: return

}

Podemos perceber o autoboxing acontecendo com o Integer.valueOf para as duas variaveis "a" e "b".
Se olharmos o código do método valueOf veremos que se os primitivos estiverem dentro de uma faixa (low e high) o objeto será buscado de um cache. Por padrão os valores do low e high são -127 e 128 respectivamente e podemos alterar o valor do high usando o parametro -Djava.lang.Integer.IntegerCache.high.

public static Integer valueOf(int i) {
 assert IntegerCache.high >= 127;
 if (i >= IntegerCache.low && i <= IntegerCache.high)
  return IntegerCache.cache[i + (-IntegerCache.low)];
 return new Integer(i);
}

Se o número estiver dentro da faixa, o objeto será sempre o mesmo, logo, a comparação com "==" será sempre true. Agora se o número estiver fora dessa faixa, o metodo valueOf sempre cria um novo objeto e a comparação com "==" será false.

Então por que no exemplo da classe C o resultado é true?
Neste caso, a comparação não é entre dois objetos e sim entre um objeto e um primitivo, isso só é possível por conta do unboxing. Vamos ver o bytecode:

class C extends java.lang.Object{
C();
  Code:
   0: aload_0
   1: invokespecial #1; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
  Code:
   0: sipush 280
   3: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   6: astore_1
   7: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   10: aload_1
   11: invokevirtual #4; //Method java/lang/Integer.intValue:()I
   14: sipush 280
   17: if_icmpne 24
   20: iconst_1
   21: goto 25
   24: iconst_0
   25: invokevirtual #5; //Method java/io/PrintStream.println:(Z)V
   28: return

}

Vemos o unboxing acontecendo no método intValue(), então nesse caso a comparação passa a ser entre dois primitivos, o que explica o resultado true.

Para não cair nesse tipo de situação, o correto seria fazer a comparação utilizando o método equals.

Nenhum comentário:

Postar um comentário