Объектные обёртки. Autoboxing

Объекты и примитивы в Java — две абсолютно разные группы типов. Виртуальная машина работает с ними по-разному. Аналогично, массивы примитивов и массивы объектов — разные типы. Generics работает только со ссылочными типами. Это значит, что можно создать, например, List<String>, List<Object>, List<byte[]>, List<int[]>, но нельзя — List<int>, т. к. int не является подтипом Object.

Так как иногда таки возникает необходимость создать список, например, целых чисел, можно создать класс, который представляет целое число.

public class Int {

    private static final int value;

    public Int(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

}

К величайшей радости, этот костыль уже написан за нас. В основном пакете, в java.lang, уже есть классы Boolean, Byte, Short, Character, Integer, Float, Long, Double. Эти классы называют объектными обёртками. Они предоставляют тот же функционал, что и примитивные типы, только делают это как объекты — посредством классов и методов.

Так можно создать, например, List<Integer>.

Autoboxing

Для преобразования между int и Integer есть методы valueOf и intValue().

Integer objTen = Integer.valueOf(10);
int iTen = objTen.intValue();

Также компилятор может сделать это автоматически:

Integer objTen = 10;
int iTen = objTen;

Автоматическое преобразование между примитивными и объектными типами называется Autoboxing. Так как объекты класса Integer иммутабельны, их можно кешировать. Но кэш всего диапазона int'ов, [-2 147 483 648, 2 147 483 647], занимал бы очень много места, поэтому Integer'ы кешируются только в диапазоне [-128, 127]. То есть Integer.valueOf(127) каждый раз возвращает один и тот же объект, а Integer.valueOf(128) каждый раз выделяет новый.

С этой особенностью связан известный и удивительный код:

Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b); // true
System.out.ptintln(c == d); // false

При этом, конечно же, c.equals(d) == true.

Пример использования

List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
System.out.println(ints); // [1, 2]

При этом можно использовать все преимущества списков — методы contains, indexOf и прочие.

Меры предосторожности

Объекты занимают больше места в памяти. Так как объектные обёртки иммутабельны, любая арифметическая операция выделяет новый объект с новым значением. Например, в этом коде всё в порядке:

long sum = 0;
for (long n : numbers) {
    sum += n;
}
System.out.println(sum);

Но если в первой строке (без причины) использовать объектную обёртку,

Long sum = 0;
for (long n : numbers) {
    sum += n; // здесь выделяется новый объект
    // sum = Long.valueOf(sum.longValue() + n)
}
System.out.println(sum);

то этот код отработает в несколько раз медленнее и выделит много новых объектов, создавая лишнюю работу для сборщика мусора.

Комментарии к уроку

Сообщить об ошибке