0

Example:

Integer x = 56; 

As I've understood it, values can be stored in an object only if you have called a field in the class or method. In the case of the Integer class it shouldn't be able to call its value field without instantiating an object since value isn't static. How this is even possible?

Henry Twist
  • 5,666
  • 3
  • 19
  • 44

2 Answers2

1

values can be stored in an object only if you have called a field in the class or method.

This statement doesn't make any sense. I can't tell if it is wrong or right, it just... doesn't make sense.

it shouldn't be able to call its value field without instantiating an object since value isn't static. I just can't wrap my head around how this is even possible.

Javac sees that and goes: Hey, you're shoving a square peg (a constant of type int) into a round hole (an object typed variable) - this doesn't make sense. Except, explicitly called out in the Java Language Specification, if an int expression shows up in a place where int expressions should not be, but an Integer expression works fine, then 'apply autoboxing'.

'apply autoboxing' means: Assume the programmer meant to write: Integer.valueOf(the expression) instead of what they wrote.

Thus:

Integer x = 56;
Integer x = Integer.valueOf(56);

are identical. They are so identical, they produce the same bytecode. That call to valueOf is in your class file, no matter how you write it. Let's test:

> cat Test.java
class Test {
    Integer x = 56;
}

> javac Test.java
 (`javap` is part of java distributions and prints bytecode)
> javap -private -c Test
Compiled from "Test.java"
class Test {
  java.lang.Integer x;

  Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: bipush        56
       7: invokestatic  #7                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      10: putfield      #13                 // Field x:Ljava/lang/Integer;
      13: return
}

Look at that - calls to the static method Integer.valueOf, even though Integer.valueOf appears exactly nowhere in our Test.java file.

So, how does Integer.valueOf work?

Essentially, it works like this:

package java.lang;

public class Integer {
    private static final Integer[] CACHE = generateCache();

    private final int value;
    public Integer(int value) {
        this.value = value;
    }

    private static Integer[] generateCache() {
        for (int i = 0; i < 256; i++) {
            CACHE[i - 128] = new Integer(i);
         }
    }

    public static Integer valueOf(int v) {
        if (v >= -128 && v < 128) return CACHE[i + 128];
        return new Integer(v);
    }
}

In other words, the Integer class pre-creates 256 integer objects, from -128 to +127 (why those values? Eh, cuz sun thought those would come up so often, it'd be worthwhile to cache them, 25 years ago). If you valueOf() with a value between -128 and +127 you get one of the caches. If you valueOf outside of it, you get a new object.

We can prove that too:

> cat Example.java
class Example {
    public static void main(String[] args) {
        Integer a = -100;
        Integer b = -100;
        Integer c = 200;
        Integer d = 200;
        System.out.println("a == b? " + (a == b));
        System.out.println("c == d? " + (c == d));
        System.out.println("ident-hash a: " + System.identityHashCode(a));
        System.out.println("ident-hash b: " + System.identityHashCode(b));
        System.out.println("ident-hash c: " + System.identityHashCode(c));
        System.out.println("ident-hash d: " + System.identityHashCode(d));
    }
}
> java Example.java
a == b? true
c == d? false
ident-hash a: 1880794513
ident-hash b: 1880794513
ident-hash c: 1991313236
ident-hash d: 736778932

This code shows that for -100, that Integer.valueOf call is returning the exact same object (== compares reference identity, i.e.: Is it the same object, when used on objects, it does not compare values. a and b are just pointing to the same object, whereas c and d are pointing at different objects that have the same values).

It's like a bunch of identical houses build in the suburbs someplace: a and b are like pieces of paper with the same address written on them, whereas c and d are like pieces of paper with a different address on each, but the two houses at these addresses look the same.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
1

The language can and does make any exceptions it wants for object types that are built into the language. In short, you can do that in Java, because Java says you can.

Specifically, the conversion of a primitive value (int, long, etc) to one of the wrapper classes (Integer, Long, etc) is called 'autoboxing' and, like the name says, is performed automatically.

Why is this allowed? Because it's highly convenient for users. Along with automatic unboxing, it allows you to get quite close to treating wrapper classes like the primitive values they contain.


Some terminology: you only 'call' methods, not variables or fields. 'Call' means 'transfer control to'.

In classes like Integer, you don't even know what fields it has. All you know is that it can store an integral value in a certain range. Values can only be entered into an Integer by calling a constructor, or one of the static methods that will return an Integer.

Documentation for Integer, in case you haven't seen it. Note that there are no operations that 'change' the value of an Integer; we say it is 'immutable'. If you want a different value, you can only make a new Integer.

iggy
  • 1,328
  • 4
  • 3