Не стоит думать, что параметризованные типы Java бесполезны — во многих случаях они делают код более четким и элегантным. Но, если вы работали на другом языке, в котором они были реализованы более «чисто», вас могут ждать разочарования. В этой главе мы изучим как достоинства, так и недостатки параметризованных типов Java, чтобы вы могли использовать эту новую возможность более эффективно.
Простая параметризация
Одной из важнейших причин для появления параметризации стало создание классов
Рассмотрим класс для хранения одного объекта. Конечно, в этом классе можно указать точный тип объекта:
//• generics/Holderl java
class Automobile {}
public class Holderl {
private Automobile a;
public Holderl(Automobile a) { this.a = a; } Automobile get() { return a; }
} ///:-
Однако такой «контейнер» получается не слишком универсальным — он не может использоваться только для одного типа. Конечно, было бы неудобно создавать новый класс для каждого типа, который нам встретится в программе.
До выхода Java SE5 можно было бы хранить в классе Object:
II: generics/Holder2.java
public class Holder2 { private Object a;
public Holder2(0bject a) { this a = a; } public void set(Object a) { this.a = a; }
public Object get О { return a; } public static void main(String[] args) {
Holder2 h2 = new Holder2(new AutomobileO); Automobile a = (Automobile)h2 get(); h2.set("He Automobile"); String s = (String)h2:get();
h2.set(l); // Автоматически упаковывается в Integer Integer x = (Integer)h2.get(),
}
} ///:-
Теперь класс Holder2 может хранить все, что угодно, — в приведенном примере один объект Holder2 используется для хранения трех разных типов данных.
В некоторых случаях бывает нужно, чтобы контейнер мог хранить объекты разных типов, но чаще контейнер предназначается для одного типа объектов. Одна из главных причин для применения параметризованных типов заключается именно в этом: вы можете указать, какой тип должен храниться в контейнере, и заданный тип будет поддерживаться комплиятором.
Итак, вместо Object в определении класса было бы удобнее использовать некий условный заменитель, чтобы отложить выбор до более позднего момента. Для этого после имени класса в угловых скобках указывается
II: generics/Holder3.java
public class Holder3
public Holder3(T a) { this.a = a; } public void set(T a) { this.a = a; } public T getО { return a; } public static void main(String[] args) { Holder3
new Holder3
}
} ///:-
При создании Holder3 необходимо указать тип объектов, хранящихся в контейнере, в угловых скобках, как в main(). В дальнейшем в контейнер можно будет помещать объекты только этого типа (или производного, так как принцип заменяемости работает и для параметризованных типов). А при извлечении вы автоматически получаете объект нужного типа.
В этом заключается основная идея параметризованных типов Java: вы указываете, какой тип должен использоваться, а механизм параметризации берет на себя все подробности.
Кортежи
При вызове метода часто требуется, чтобы метод возвращал несколько объектов. Команда return позволяет вернуть только один объект, поэтому проблема решается созданием объекта, содержащего несколько возвращаемых объектов. Конечно, можно создавать специальный класс каждый раз, когда возникает подобная ситуация, но параметризованные типы позволяют решить проблему один раз и избавиться от хлопот в будущем. Заодно решается проблема безопасности типов на стадии компиляции.
Концепция нескольких объектов, «упакованных» в один объект, называется
Обычно кортеж может иметь произвольную длину, а все объекты кортежа могут относиться к разным типам. Однако мы хотим задать тип каждого объекта и при этом гарантировать, что при чтении значения будет получен правильный тип. Для решения проблемы переменной длины мы создадим несколько разных кортежей. Вот один из них, рассчитанный на два объекта:
//: net/mi ndvi ew/uti1/TwoTuple.java
package net.mindview.util;