Эккель Брюс
Шрифт:
Итак, вместо Object в определении класса было бы удобнее использовать некий условный заменитель, чтобы отложить выбор до более позднего момента. Для этого после имени класса в угловых скобках указывается параметр типа, который при использовании заменяется фактическим типом. В нашем примере это будет выглядеть так (Т — параметр типа):
II: generics/Holder3.java
public class Holder3<T> { private T a;
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<Automobile> h3 =
new Holder3<Automobile>(new AutomobileO); Automobile a = h3.get; // Преобразование не требуется // h3.set("He Automobile"); // Ошибка // h3.set(1); // Ошибка
}
} ///:-
При создании Holder3 необходимо указать тип объектов, хранящихся в контейнере, в угловых скобках, как в main. В дальнейшем в контейнер можно будет помещать объекты только этого типа (или производного, так как принцип заменяемости работает и для параметризованных типов). А при извлечении вы автоматически получаете объект нужного типа.
В этом заключается основная идея параметризованных типов Java: вы указываете, какой тип должен использоваться, а механизм параметризации берет на себя все подробности.
Кортежи
При вызове метода часто требуется, чтобы метод возвращал несколько объектов. Команда return позволяет вернуть только один объект, поэтому проблема решается созданием объекта, содержащего несколько возвращаемых объектов. Конечно, можно создавать специальный класс каждый раз, когда возникает подобная ситуация, но параметризованные типы позволяют решить проблему один раз и избавиться от хлопот в будущем. Заодно решается проблема безопасности типов на стадии компиляции.
Концепция нескольких объектов, «упакованных» в один объект, называется кортежем (tuple). Получатель объекта может читать элементы, но не может добавлять их (эта концепция еще называется объектом передачи данных).
Обычно кортеж может иметь произвольную длину, а все объекты кортежа могут относиться к разным типам. Однако мы хотим задать тип каждого объекта и при этом гарантировать, что при чтении значения будет получен правильный тип. Для решения проблемы переменной длины мы создадим несколько разных кортежей. Вот один из них, рассчитанный на два объекта:
//: net/mi ndvi ew/uti1/TwoTuple.java
package net.mindview.util;
public class TwoTuple<A,B> { public final A first; public final В second;
public TwoTuple(A а, В b) { first = a; second = b; } public String toStringO {
return "(" + first + " + second + ")";
}
} ///:-
Конструктор запоминает сохраняемый объект, а вспомогательная функция toStringO выводит значения из списка. Обратите внимание: кортеж подразумевает упорядоченное хранение элементов.
При первом чтении может показаться, что такая архитектура нарушает общие принципы безопасности программирования на Java. Разве first и second не должны быть объявлены приватными, а обращения к ним осуществляться только из методов getFirst и getSecond? Подумайте, какая безопасность реализуется в этом случае: клиент может читать объекты и делать с прочитанными значениями все, что пожелает, но не может изменить first и second. Фактически объявление final делает то же самое, но короче и проще.
Кортежи большей длины создаются посредством наследования. Добавить новый параметр типа несложно:
//: net/mi ndvi ew/uti1/ThreeTuple.java
package net.mi ndvi ew.uti1;
public class ThreeTuple<A.B,C> extends TwoTuple<A,B> { public final С third; public ThreeTuple(A а, В b, С с) { super(a. b); third = c:
}
public String toStringO {
return "(" + first + " + second + ", " + third +")";
}
// net/mi ndvi ew/uti1/FourTuple. java package net.mindview.util;
public class FourTuple<A,B,C,D> extends ThreeTuple<A,B,C> { public final D fourth; public FourTuple(A а, В b. С с. D d) { super(a. b, c), fourth = d;
}
public String toStringO {
return "(" + first + ", " + second + " + third + " + fourth + ")";
}
} Hill . net/mi ndvi ew/uti1/Fi veTuple.java package net.mindview util;
public class FiveTuple<A,B,C,D,E> extends FourTuple<A,В,C.D> { public final E fifth;
public FiveTuple(A а. В b. С с. D d. E e) { super(a, b. c. d); fifth = e.