Поддержка JavaScript отключена
SBP-Program | |||||
На главную -> Java |   | ||||
  |
При создании коллекции можно указать тип объектов, которые будут храниться в ней. Пример: Напечатает “str1”. Строка Vector < String> vect; говорит о том, что вектор будет содержать только строки. Вот почему в строке String str = vect.get(0); можно обойтись без приведения типов. При создании объекта вектора указываем параметр String: vect = new Vector < String>();. Главная цель шаблонов – type-safety, т.е. правильное соотношение типов. В предыдущем примере однозначно был указан параметр вектора. Есть случаи, где удобнее использовать формальный параметр, подобно формальным аргументам функций. Пример: Создан параметризованный класс FormalVector < E>. E – формальный параметр. При создании экземпляра класса вместо E указываем конкретный тип. Простой пример: Напечатает “str1”. В классе StringValues создаётся экземпляр класса FormalVector < String>. Вместо формального параметра E указываем String. Теперь класс FormalVector будет хранить и работать с типом String. Можно создать экземпляр класса FormalVector с другим параметром, например, Integer: FormalVector < Integer> vect = new FormalVector < Integer>();. Это значит, что FormalVector < Integer> будет работать с типом Integer. Все экземпляры параметризованного класса представляют один тип, независимо от фактического значения параметров. Важное правило: если класс A есть потомок класса B, то это не означает, что aClass < A> есть потомок aClass < B>. Шаблоны могут применяться и в методах. Типы аргументов можно делать параметрическими. Пример: void aMethod(Vector < E> vect);
или так: void aMethod(Vector < ?> vect);
Рекомендуется использовать неизвестный параметр < ?> в случаях, когда от него не зависят другие аргументы и возвращаемое значение. Если такая зависимость имеется, то следует использовать формальный параметр < E>. Возможно использовать вместе неизвестный параметр < ?> и формальный параметр < E> : pablic < E> void aMethod(Vector < E> vect, List < ? extends E> list);
здесь аргумент list имеет ограниченный неизвестный параметр < ? extends E>, но имеется связь между двумя аргументами – это формальный параметр < E>. Вместо формального параметра можно указать знак вопроса, т.е. неизвестный параметр. Изменим класс StringValues(см. выше): Напечатает “str1”. Новым для нас является аргумент метода private void printValues(FormalVector < ?> vect)
Знак вопроса вместо какого-то типа говорит о том, что здесь может оказаться любой тип, т.е. FormalVector < ?> — это супертип для любого конкретного типа, например, для FormalVector < String>. Обратим внимание на первую строку метода: Object str = vect.getVal(0);
здесь str имеет тип Object. Это позволяет безопасно получать элементы коллекции методом getVal, ведь любые объекты представляют тип Object. Неизвестный параметр может примениться и при объявлении полей: private Vector < ?> aVector;
Ограниченный неизвестный параметр указывает, что вместо него можно подставить не любой тип, а только тип, наследуемый от определённого класса. Пример: Vector < ? extends aClass>
здесь на место неизвестного параметра ? можно поставить aClass или его наследников, пусть даже непрямых. aClass в этом случае называется верхней границей неизвестного параметра. Другой пример: Vector < ? super aClass>
Параметр вида < ? super aClass> называется нижняя граница. Фактический параметр должен быть родительским по отношению к aClass, или должен быть самим aClass. Рассмотрим формальный пример. Создадим три пустых класса: ClassA public class ClassA{}
его наследника ClassB public class ClassB extends ClassA{}
ClassC, как наследника от ClassB: public class ClassC extends ClassB{}
Почему классы пустые? В этом примере важно увидеть отношения наследования между классами, а не содержимое этих классов. Теперь создадим класс MainClass, а в нём метод static void addObject(Vector < ? extends ClassA> vect){}
В методе addObject имеем ограниченный неизвестный параметр < ? extends ClassA>, который говорит нам, что аргументом может быть любой вектор, содержащий объекты класса ClassA или его наследников. А таковыми и являются наши три пустых класса: ClassA, ClassB, ClassC. В коде метода main создадим объект класса ClassA ClassA ca = new ClassA();
вектор, содержащий объекты класса ClassA Vector < ClassA> vectA = new Vector < ClassA>();
добавим ca к vectA vectA.add(ca);
Такой вектор соответсвует определению аргумента метода addObject, к которому мы и обращаемся: addObject(vectA);
Для классов ClassB, ClassC действия аналогичные. Вот полный код класса MainClass: Если метод addObject объявить так: static void addObject(Vector < ClassA> vect){}
т.е. без ограниченного неизвестного параметра, то получим сообщения об ошибках в строкахaddObject(vectB); и addObject(vectC);(несоответствие типов аргументов). Почему? Смотрите пункт “Шаблоны и подтипы”. Vector < ClassB> и Vector < ClassC> есть подтипы не Vector < ClassA>, а Vector < ? extends ClassA>. Объявляя класс-коллекцию, мы указываем параметр, т.е. тип объектов, хранящихся в нём. Если не указать параметр, то можем получить предупреждение “raw type”. Пример: Можно объявлять коллекции без указания параметра, это сделано для работы с прежним кодом, когда ещё не было шаблонов.Ещё пример: В последней строке получаем предупреждение, которое приведёт к ошибке при попытке извлечь строку из vect: System.out.print((String) vect.get(0));
Сообщение об ошибке: java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String.Исправить положение можно так: указать параметр для vect, Double привести к строке Это значит, что компилятор не гарантирует соответствие типов. Пример: Мы не знаем, что будет храниться в aVect, там может оказаться и не строка, отсюда предупреждение. Исправляем положение, указав параметр для aVect: Соглашение по именам параметровПараметрические типы принято обзначать T, если имеется несколько параметров, то для следующих используют буквы, близкие к T, т.е. S, U, V и т.д. Элементы коллекций обозначают буквой E. Ключи – K, значения — V, числа – N. Во всех случаях параметры обозначают большими буквами. Программирование |
  | |||
 
|
 
|
||||