и компилятор извлечет из объекта k1 класса Integer числовое значение 55. Конечно, для этого компилятор обратится к методу intValue () класса Integer, но это незаметно для программиста.
Автоматическая упаковка и распаковка возможна и в методах классов. Рассмотрим простой класс.
class AutoBox{ static int f(Integer value){ return value; // Распаковка.
}
public static void main(String[] args){
Integer n = f(55);
}
}
В методе main() этого примера сначала число 55 приводится к типу параметра метода f() с помощью упаковки. Затем результат работы метода f () упаковывается в объект n класса Integer.
Автоматическую упаковку и распаковку можно использовать в выражениях, написав k1++ или даже (k1 + k2 / k1), но это уже слишком! Представьте себе, сколько упаковок и распаковок вставит компилятор и насколько это замедлит работу программы!
Настраиваемые типы (generics)
Введение в язык Java автоматической упаковки типов позволило определить еще одну новую конструкцию —
class MyGenericClass
public MyGenericClass(){}
public MyGenericClass(T data){ this.data = data;
}
public T getData(){ return data;
}
public void setData(T data){ this.data = data;
}
}
в котором есть поле data неопределенного пока типа, обозначенного буквой T. Разумеется, можно написать другую букву или даже идентификатор. Буква T появилась просто как первая буква слова Type.
Перед использованием такого класса-шаблона его надо настроить, задав при обращении к его конструктору определенный тип в угловых скобках. Например:
class MyGenericClassDemo{
public static void main(String[] args){
MyGenericClass
Integer n = iMyGen.getData();
MyGenericClass
Double x = dMyGen.getData();
}
}
Если при определении экземпляра настраиваемого класса и слева и справа от знака равенства в угловых скобках записан один и тот же тип, то справа его можно опустить для краткости записи, оставив только пару угловых скобок (так называемый "ромбовидный оператор", "diamond operator"). Используя это новое, введенное в Java 7, сокращение, предыдущий класс можно записать так:
class MyGenericClassDemo{
public static void main(String[] args){
MyGenericClass
Integer n = iMyGen.getData();
MyGenericClass
Double x = dMyGen.getData();
}
}
Рассмотрим более содержательный пример. Пусть нам надо вычислять среднее арифметическое значение нескольких чисел, причем в одном случае это целые числа, в другом — вещественные, в третьем — короткие или, наоборот, длинные целые числа. У среднего значения в любом случае будет тип double. В листинге 4.2 написан один общий класс-шаблон для всех этих случаев.
Листинг 4.2. Настраиваемый класс
class Average
public Average(T[] data) { this.data = data; }
public double average(){ double result = 0.0;
for (T t: data) result += t.doubleValue(); return result / data.length;
}
public static void main(String[] args){
Integer[] iArray = {1, 2, 3, 4};
Double[] dArray = {3.4, 5.6, 2.3, 1.24};
Average
}
Обратите внимание на то, что в заголовке класса в угловых скобках указано, что тип T — подкласс класса Number. Это сделано потому, что здесь тип T не может быть произвольным. Действительно, в методе average ( ) использован метод doubleValue ( ) класса Number, а это означает, что тип T ограничен классом Number и его подклассами. Кроме того, операции сложения и деления тоже допустимы только для чисел.
Конструкция
У настраиваемого типа может быть более одного параметра. Они перечисляются в угловых скобках через запятую:
class MyGenericClass2{ private S id; private T data;
public MyGenericClass2() {}
public MyGenericClass2(S id, T data){ this.id = id; this.data = data;
}
public S getId(){ return id;
}
public void setId(S data){ this.id = id;
}
public T getData(){ return data;
}
public void setData(T data){ this.data = data;
}
}