Начиная с версии Java SE 5 в JDK входит пакет java.util.concurrent.atomic, в котором, в частности, есть классы AtomicInteger и AtomicLong, обеспечивающие изменение числового значения этих классов на уровне машинных команд. Начальное значение задается конструкторами этих классов. Затем методами addAndGet ( ), getAndAdd ( ), incrementAndGet ( ), getAndnIncrement(), decrementAndGet(), getAndDecrement, getAndSet(), set() можно изменять это значение.
Автоматическая упаковка и распаковка типов
В листинге 4.1 объекты числовых классов создавались статическим методом, в котором указывалось числовое значение объекта:
Integer k1 = Integer.valueOf(55);
Это правильно с точки зрения объектно-ориентированного программирования, но утомительно для программиста. Начиная с пятой версии Java, было решено упростить такую запись. Теперь можно писать
Integer k1 = 55;
как будто k 1— простая числовая переменная примитивного типа. Ничего нового в язык Java такая запись не вносит: компилятор, увидев ее, тут же восстановит применение статического метода. Но она облегчает работу программиста, предоставляя ему привычную форму определения переменной. Как говорят, компилятор делает автоматическую упаковку (auto boxing) числового значения в объект. Компилятор может сделать и автоматическую распаковку. После приведенных ранее определений объекта k1 можно написать, например,
int n = k1;
и компилятор извлечет из объекта 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.
Автоматическую упаковку и распаковку можно использовать в выражениях, написав k 1++ или даже (k 1+ k 2/ k 1), но это уже слишком! Представьте себе, сколько упаковок и распаковок вставит компилятор и насколько это замедлит работу программы!
Настраиваемые типы (generics)
Введение в язык Java автоматической упаковки типов позволило определить еще одну новую конструкцию — настраиваемые типы (generics), позволяющие создавать шаблоны классов, интерфейсов и методов. Например, можно записать обобщенный настраиваемый (generic) класс
class MyGenericClass{ private T data;
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 iMyGen = new MyGenericClass(55);
Integer n = iMyGen.getData();
MyGenericClass dMyGen = new MyGenericClass(-37.3456);
Double x = dMyGen.getData();
}
}
Если при определении экземпляра настраиваемого класса и слева и справа от знака равенства в угловых скобках записан один и тот же тип, то справа его можно опустить для краткости записи, оставив только пару угловых скобок (так называемый "ромбовидный оператор", "diamond operator"). Используя это новое, введенное в Java 7, сокращение, предыдущий класс можно записать так:
class MyGenericClassDemo{
public static void main(String[] args){
MyGenericClass iMyGen = new MyGenericClass<>(55);
Integer n = iMyGen.getData();
MyGenericClass dMyGen = new MyGenericClass<>(-37.3456);
Double x = dMyGen.getData();
}
}
Рассмотрим более содержательный пример. Пусть нам надо вычислять среднее арифметическое значение нескольких чисел, причем в одном случае это целые числа, в другом — вещественные, в третьем — короткие или, наоборот, длинные целые числа. У среднего значения в любом случае будет тип double. В листинге 4.2 написан один общий класс-шаблон для всех этих случаев.
Листинг 4.2. Настраиваемый класс
class Average{ T[] data;
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 iAver = new Average<>(iArray); System.out.println("int average = " + iAver.average()); Average dAver = new Average<>(dArray); System.out.println("double average = " + dAver.average());
}
Обратите внимание на то, что в заголовке класса в угловых скобках указано, что тип T — подкласс класса Number. Это сделано потому, что здесь тип T не может быть произвольным. Действительно, в методе average ( ) использован метод doubleValue ( ) класса Number, а это означает, что тип T ограничен классом Number и его подклассами. Кроме того, операции сложения и деления тоже допустимы только для чисел.
Читать дальше
Конец ознакомительного отрывка
Купить книгу