Spec-Zone .ru
спецификации, руководства, описания, API
|
Как Вы уже знаете, возможно присвоить объект одного типа к объекту другого типа при условии, что типы являются совместимыми. Например, можно присвоить Integer Object, так как Object является одним из супертипов Integer:
Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // OK
В объектно-ориентированной терминологии это вызывают, "" отношение. Так как Integer является своего рода Object, присвоение позволяется. Но Integer является также своего рода Number, таким образом, следующий код допустим также:
public void someMethod(Number n) { /* ... */ } someMethod(new Integer(10)); // OK someMethod(new Double(10.1); // OK
То же самое является также истиной с обобщениями. Можно выполнить универсальный вызов типа, передавая Number как его параметр типа, и любой последующий вызов add будет позволен, если параметр будет совместимым с Number:
Box<Number> box = new Box<Number>(); box.add(new Integer(10)); // OK box.add(new Double(10.1)); // OK
Теперь рассмотрите следующий метод:
public void boxTest(Box<Number> n) { /* ... */ }
Какой параметр это принимает? Смотря на его подпись, можно видеть, что это принимает единственный параметр, типом которого является Box<Number>. Но что это означает? Вам разрешают передать в Box<Integer> или Box<Double>, как Вы могли бы ожидать? Ответ является "нет", becaue Box<Integer> и Box<Double> не являются подтипами Box<Number>.
Это - распространенное заблуждение когда дело доходит до программирования с обобщениями, но это - важное понятие, чтобы учиться.
Можно выделить подтипы в универсальном class или взаимодействовать через интерфейс, расширяясь или реализовывая его. Отношение между параметрами типа одного class или интерфейса и параметрами типа другого определяется пунктами implements и extends.
Используя классы Collections как пример, ArrayList<E> реализует List<E>, и List<E> extends Collection<E>. Таким образом, ArrayList<String> является подтипом List<String>, который является подтипом Collection<String>. Пока Вы не изменяете параметр типа, отношение выделения подтипов сохраняется между типами.
Теперь предположите, что мы хотим определить наш собственный интерфейс списка, PayloadList, который связывает дополнительное значение универсального типа P с каждым элементом. Его объявление могло бы быть похожим:
interface PayloadList<E,P> implements List<E> { void setPayload(int index, P val); ... }
Следующая параметризация PayloadList является подтипами List<String>: