Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java box then widen on == operator

Tags:

java

Number n = 10; 
int i = 10;
System.out.println(n == i);

Based on "You CAN box then widen". Why the above code gives a compile time error? My guess is that if i will be first boxed to Integer and widened to Number the result will always be false. There are any specification referring to == operator when comparing a primitive with an object? Will always try to perform unboxing and the if necessarily will widen?

like image 647
L4zy Avatar asked Dec 11 '22 11:12

L4zy


2 Answers

Per the JLS, == comparisons between a boxed and an unboxed value result in an unboxing conversion, not the other way around (or else you'd be using reference equality, not value equality). The compiler can't unbox a plain Number; Number itself isn't "convertible to a numeric type."

The Java 7 compiler appears to be being clever. The assembly output for this version and for the version in which the comparison is moved into a private method completely ignore the declared type Number, and everything works properly. Make that method public, and the behavior is as specified: The conversion isn't one of the listed types for which unboxing will occur, and the compiler boxes 10 to an Integer and compares by reference, meaning that if you try using Integer.valueOf(10), you'll get true for the range -128..127, and if you use anything else (another width, new Integer(10)), you'll get false.

Output from your code (note that Number appears nowhere, and that you're getting comparison based on reference equality in line 18; try using L or casting to short):

public static void main(java.lang.String[]);
  Code:
   0: bipush        10
   2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   5: astore_1      
   6: bipush        10
   8: istore_2      
   9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
  12: aload_1       
  13: bipush        10
  15: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  18: if_acmpne     25
  21: iconst_1      
  22: goto          26
  25: iconst_0      
  26: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
  29: return

Version that prevents optimization:

public class Test
{
    public static void main(String[] args)
    {
        Number n = new Integer(10);
        compare(n);
    }

    public static void compare(Number n)
    {
        int i=10;
        System.out.println(n == 10);
    }
}

Assembly; note that you're still getting a reference comparison in line 12:

public static void main(java.lang.String[]);
  Code:
   0: new           #2                  // class java/lang/Integer
   3: dup           
   4: bipush        10
   6: invokespecial #3                  // Method java/lang/Integer."<init>":(I)V
   9: astore_1      
  10: aload_1       
  11: invokestatic  #4                  // Method compare:(Ljava/lang/Number;)V
  14: return        

public static void compare(java.lang.Number);
  Code:
   0: bipush        10
   2: istore_1      
   3: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
   6: aload_0       
   7: bipush        10
   9: invokestatic  #6                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  12: if_acmpne     19
  15: iconst_1      
  16: goto          20
  19: iconst_0      
  20: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
  23: return
like image 174
chrylis -cautiouslyoptimistic- Avatar answered Jan 03 '23 19:01

chrylis -cautiouslyoptimistic-


The code in your question gives me "Incompatible operand types Number and int" with Java 6 and 7 using the Eclipse compiler. With javac from Oracle's Java 7 SDK, it compiles and prints true.

Why?

The assignment Number n = 10 will be converted into Number n = Integer.valueOf(10);

Later, the compiler will create n == Integer.valueOf(10) (autoboxing the int value).

This gives true because Integer.valueOf() keeps an internal cache for small integer numbers and always returns the same instance for them:

Integer.valueOf(10) == Integer.valueOf(10)

But this is a just a side effect of the implementation, you should not rely on it.

Bytecode:

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1      
       6: bipush        10
       8: istore_2      
       9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      12: aload_1       
      13: iload_2       
      14: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      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        
like image 36
Aaron Digulla Avatar answered Jan 03 '23 20:01

Aaron Digulla