1. Что такое дженерики
Скачать 0.84 Mb.
|
7. Дженерики 1. Что такое дженерики? Generics - набор свойств языка позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры (T – параметр в котором могут быть разные объекты). ArrayList Свойства Generics: - Строгая типизация. - Единая реализация. - Отсутствие информации о типе. Ограничения: - - в <> могут быть только ссылочные типы, можно String, Integer, int [] array, int, double, 10; - - внутри класса или метода нельзя создать экземпляр класса, массив или использовать метод equals, так как внутри класса не известно о самом классе – компилятор не может понять тип - ограничение наследования Пусть у нас есть тип Foo, который является подтипом Bar, и еще G - наследник Коллекции. То G - не может быть параметром статического класса - Не может Создать, Поймать, или Бросить Объекты Параметризованных Типов class MathException - У class не может быть двух перегруженных методов, у которых будет та же самая подпись после стирания типа. public class Example { public void print(Set } Примером использования обобщенных типов может служить Java Collection Framework. Так, класс LinkedList LinkedList, ничего не говоря о типе элемента в списке, предлагается использовать точное указание типа LinkedList 2. Для чего нужны дженерики? Позволяет осуществлять проверку на правильность написания кода во время компиляции, а не в Runtime. Возможность создавать универсальные алгоритмы и структуры данных. Сделали использование Java Collection Framework проще, удобнее и безопаснее С их помощью можно объявлять классы, интерфейсы и методы, где тип данных указан в виде параметра. generics (обобщенные типы и методы) позволяют нам уйти от жесткого определения используемых типов. Компилятор стирает все дженерики. В Runtime дженериков практически нет. Компилятор использует кастование 3. Что такое сырые типы (raw type)? Сырые типы — это типы без указания "уточненения" в фигурных скобках. Нужны чтобы поддерживать старый код (обратная совместимость). Рекомендуется использовать хоть какие-то параметризованные типы. Raw type - это имя интерфейса без указания параметризованного типа: Также Diamond синтаксис связан с понятием "Type Inference", или же выведение типов. Ведь компилятор, видя справа <> смотрит на левую часть, где расположено объявление типа переменной, в которую присваивается значение. И по этой части понимает, каким типом типизируется значение справа. List На самом деле, если в левой части указан дженерик, а справа не указан <>, компилятор сможет вывести тип. Однако это будет смешиванием нового стиля с дженериками и старого стиля без них - вы теряете безопасность ( типобезопасность) типов (может добавляться какой угодно тип в список, а не то что надо. Когда будет доставаться – то может быть сюрприз))) ArrayList ArrayList arrayList = new ArrayList(); // raw type arrayList = strings; // Ok strings = arrayList; // Unchecked assignment (назначение) arrayList.add(1); //unchecked call 4. Что такое вайлдкарды (Маски)? Языковая конструкция внутри даймонд-оператора, позволяющая сделать код более универсальным. Решает проблему наследования типов в дженериках. (коллекция <Интежер> не наследник коллекции <Намбер>) Может быть 3-х типов: инвариантность, аппер и ловер Class: Cat / Dog -> Pet -> Animal -> Object extends Animal> - тип ? является любой наследник Animal (Upper Bounded (ограничение сверху) Wildcards) super Cat> - тип ? является любой родитель Cat, включая Cat (Lower Bounded Wildcards). Dog не подходит. List extends Number> numberList = new ArrayList<>(); List } Означает, что мы можем присвоить в numberList листы Не положить туда Number и наследники, а присвоить!! Туда безопасно можем положить только null. Компилятор стирает дженерик типы и видит List extends Number>. Т.е. безопасно будет: List extends Number> numberList = new Number<>(); Компилятор видит будещее, что если мы положим в list Лонг, а затем можем положить туда Интежер – будет не безопасно. Он кладет только то, что безопасно – null List super Number> numberList = new ArrayList<>(); // можно ложить в лист Интежер, Лонг и т.п., нельзя Обжект Компилятор видит будущее, мы можем присвоить сюда Листы Намберов и Обжектов. Если мы положим в Лист Намберов Интежер, Лонг, Дабл – то ошибки не будет, а если положить туда Обжект – ошибка компиляции 5. Расскажите про принцип PECS The Get and Put Principle или PECS (Producer Extends Consumer Super) Get and Put Principe Из одного типа переменных можно только читать, в другой — только вписывать (исключением является возможность записать null для extends и прочитать Object для super). - > Запись вида Collection> равносильна Collection extends Object> , а значит — коллекция может содержать объекты любого класса, так как все классы в Java наследуются от Object – поэтому подстановка называется неограниченной. - Ковариация - Если мы объявили wildcard с extends, то это producer. Он только «продюсирует», предоставляет элемент из контейнера, а сам ничего не принимает. - Контрвариация - Если же мы объявили wildcard с super — то это consumer. Он только принимает, а предоставить ничего не может. Если метод имеет аргументы с параметризованным типом (например, Collection или Predicate), то в случае, если аргумент - производитель (producer), нужно использовать ? extends T, а если аргумент - потребитель (consumer), нужно использовать ? super T. Eсли метод читает данные из аргумента, то этот аргумент - производитель, Метод передаёт данные в аргумент, то аргумент является потребителем. Важно заметить, что определяя производителя или потребителя, мы рассматриваем только данные типа T. // Ковариантность, значит producer - аргумент nums отдает (производит) в метод ресурсы public static double sum(Collection extends Number> nums) { double s = 0.0; for (Number num : nums) s += num.doubleValue(); return s;} List List List Arrays. //Контрвариантность - если аргумент метода потребляет в методе что-то /(возможно, изменяется сам) public static void count(Collection super Integer> ints, int n) { for (int i = 0; i < n; i++) ints.add(i); } List List List Интерфейс Collection расширяют интерфейсы: • List (список) представляет собой коллекцию, в которой допустимы дублирующие значения. Элементы такой коллекции пронумерованы, начиная от нуля, к ним можно обратиться по индексу. Реализации: o ArrayList - инкапсулирует в себе обычный массив, длина которого автоматически увеличивается при добавлении новых элементов. o LinkedList (двунаправленный связный список) - состоит из узлов, каждый из которых содержит как собственно данные, так и две ссылки на следующий и предыдущий узел. o Vector — реализация динамического массива объектов, методы которой синхронизированы (доступны по очереди, т.е. может использовать кто-то один). o Stack — реализация стека LIFO (last-in-first-out). • Set (сет) описывает неупорядоченную коллекцию, не содержащую повторяющихся элементов. Реализации: o HashSet - использует HashMap для хранения данных. В качестве ключа и значения используется добавляемый элемент. Из-за особенностей реализации порядок элементов не гарантируется при добавлении. o LinkedHashSet — гарантирует, что порядок элементов при обходе коллекции будет идентичен порядку добавления элементов. o (SortedSet) TreeSet — предоставляет возможность управлять порядком элементов в коллекции при помощи объекта Comparator, либо сохраняет элементы с использованием «natural ordering». • Queue (очередь) предназначена для хранения элементов с предопределённым способом вставки и извлечения FIFO (first-in-first-out): o PriorityQueue — предоставляет возможность управлять порядком элементов в коллекции при помощи объекта Comparator, либо сохраняет элементы с использованием «natural ordering». o ArrayDeque — реализация интерфейса Deque, который расширяет интерфейс Queue методами, позволяющими реализовать конструкцию вида LIFO (last-in- first-out). Интерфейс Map реализован классами: • Hashtable — хэш-таблица, методы которой синхронизированы. Не позволяет использовать null в качестве значения или ключа и не является упорядоченной. • HashMap — хэш-таблица. Позволяет использовать null в качестве значения или ключа и не является упорядоченной. • LinkedHashMap — упорядоченная реализация хэш-таблицы. • TreeMap — реализация, основанная на красно-чёрных деревьях. Является упорядоченной и предоставляет возможность управлять порядком элементов в коллекции при помощи объекта Comparator, либо сохраняет элементы с использованием «natural ordering». • WeakHashMap — реализация хэш-таблицы, которая организована с использованием weak references для ключей (сборщик мусора автоматически удалит элемент из коллекции при следующей сборке мусора, если на ключ этого элемента нет жёстких ссылок). 3. Почему Map — это не Collection, в то время как List и Set являются Collection? Collection представляет собой совокупность некоторых элементов. Map - это совокупность пар «ключ- значение». 4. В чем разница между классами java.util.Collection и java.util.Collections? Collections - набор статических методов для работы с коллекциями. Collection - один из основных интерфейсов Java Collections Framework 5. Какая разница между итераторами с fail-fast и fail-safe поведением? (С примерами) fail-fast поведение означает, что при возникновении ошибки или состояния, которое может привести к ошибке, система немедленно прекращает дальнейшую работу и уведомляет об этом. Использование fail-fast подхода позволяет избежать недетерминированного поведения программы в течение времени. В Java Collections API некоторые итераторы ведут себя как fail-fast и выбрасывают ConcurrentModificationException, если после его создания была произведена модификация коллекции, т.е. добавлен или удален элемент напрямую из коллекции, а не используя методы итератора. Реализация такого поведения осуществляется за счет подсчета количества модификаций коллекции (modification count): - при изменении коллекции счетчик модификаций так же изменяется; - при создании итератора ему передается текущее значение счетчика; - при каждом обращении к итератору сохраненное значение счетчика сравнивается с текущим, и, если они не совпадают, возникает исключение. Итераторы по умолчанию для Collections из java.util package , такие как ArrayList , HashMap и т. д., Являются Fail-Fast ArrayList Iterator Integer number = iterator.next(); numbers.add(50); } В приведенном выше фрагменте кода ConcurrentModificationException генерируется в начале следующего цикла итерации после выполнения модификации. итераторы fail-safe не вызывают никаких исключений при изменении структуры, потому что они работают с клоном коллекции вместо оригинала. Итератор коллекции CopyOnWriteArrayList и итератор представления keySet коллекции ConcurrentHashMap являются примерами итераторов fail-safe. 6. Чем различаются Enumeration (устаревший) и Iterator? Хотя оба интерфейса и предназначены для обхода коллекций между ними имеются существенные различия: - с помощью Enumeration нельзя добавлять/удалять элементы; - в Iterator исправлены имена методов для повышения читаемости кода (Enumeration.hasMoreElements() соответствует Iterator.hasNext(), Enumeration.nextElement() соответствует Iterator.next() и т.д); - Enumeration присутствуют в устаревших классах, таких как Vector/Stack, тогда как Iterator есть во всех современных классах-коллекциях. 7. Как между собой связаны Iterable, Iterator и «for-each»? Интерфейс Iterable имеет только один метод - iterator(), который возвращает объект-Iterator. Iterable Iterator Класс Iterator отвечает за безопасный проход по списку элементов, имеет всего 3 метода: hasNext() — возвращает true или false в зависимости от того, есть ли в списке следующий элемент, или мы уже дошли до последнего. next() — возвращает следующий элемент списка remove() — удаляет элемент из списка Классы, реализующие интерфейс Iterable, могут применяться в конструкции for-each, которая использует Iterator. // Iterating over collection 'c' using Iterator for (Iterator i = c.iterator(); i.hasNext(); ) System.out.println(i.next()); 8. Можно ли итерируясь по ArrayList удалить элемент? Какое вылетит исключение? Если не через iterator, например через for each - то будет исключение В Java для удаления элементов во время перебора нужно использовать специальный объект — итератор (класс Iterator) for (Cat cat: cats) { if (cat.name.equals("Бегемот")) { cats.remove(cat); } } java.util. ConcurrentModificationException Iterator Cat nextCat = catIterator.next();//получаем следующий элемент System.out.println(nextCat);//выводим его в консоль } 9. Как поведёт себя коллекция, если вызвать iterator.remove()? Если вызову iterator.remove() предшествовал вызов iterator.next(), то iterator.remove() удалит элемент коллекции, на который указывает итератор, в противном случае будет выброшено IllegalStateException(). Iterator String el = it.next(); if (el.equals("")) { for (String el: names) { if (el.equals("")) { names.remove(el); // WRONG! } it.remove(); // it’s norm } } 10. Чем Set отличается от List? Set описывает неупорядоченную коллекцию, не содержащую повторяющихся элементов List представляет собой коллекцию, в которой допустимы дублирующие значения. Элементы такой коллекции пронумерованы, начиная от нуля, к ним можно обратиться по индексу. 11. Расскажите про интерфейс Set. Представляет собой неупорядоченную коллекцию, которая не может содержать дублирующиеся данные. Является программной моделью математического понятия «множество». Set не добавляет новых методов, только вносит изменения в унаследованные. Множество, как и список, и любую коллекцию (и не только) можно обойти в цикле for-each: for (Integer number : intSet) { System.out.println(number); } Но вот доступ по индексу (порядковому номеру) (метод list.get(int index)) есть только у List, у Set этого метода нет. Это связано с тем, что порядок элементов во множестве не определён. Интерфейс Set включает следующие методы: Метод Описание add(Object o) Добавление элемента в коллекцию, если он отсутствует. Возвращает true, если элемент добавлен. addAll(Collection c) Добавление элементов коллекции, если они отсутствуют. clear() Очистка коллекции. contains(Object o) Проверка присутствия элемента в наборе. Возвращает true, если элемент найден. containsAll(Collection c) Проверка присутсвия коллекции в наборе. Возвращает true, если все элементы содержатся в наборе. equals(Object o) Проверка на равенство. hashCode() Получение hashCode набора. isEmpty() Проверка наличия элементов. Возвращает true если в коллекции нет ни одного элемента. iterator() Функция получения итератора коллекции. remove(Object o) Удаление элемента из набора. removeAll(Collection c) Удаление из набора всех элементов переданной коллекции. retainAll(Collection c) Удаление элементов, не принадлежащих переданной коллекции. size() Количество элементов коллекции toArray() Преобразование набора в массив элементов. toArray(T[] a) Преобразование набора в массив элементов. В отличии от предыдущего метода, который возвращает массив объектов типа Object, данный метод возвращает массив объектов типа, переданного в параметре. |