1. Почему - причина введения общих механизмов
Если мы хотим реализовать строковый массив и потребуем от него динамически изменить размер, мы все подумаем об использовании ArrayList для агрегирования строковых объектов. Однако через некоторое время мы хотим реализовать массив объектов даты, размер которых может быть изменен. В настоящее время мы, безусловно, надеемся, что сможем повторно использовать реализацию ArrayList для строковых объектов, которые я писал ранее.
До Java 5 реализация ArrayList является примерно следующим образом:
открытый класс ArrayList {public Object get (int i) {...} public void Add (Object o) {...} ... Private Object [] elementData;}Из приведенного выше кода мы видим, что функция ADD, используемая для добавления элементов в ArrayList, получает параметр типа объекта. Метод GET, который получает указанный элемент из ArrayList, также возвращает объект объекта. MARTION MARTION ELEMENDATA объекта хранит объект в ArrayList. То есть независимо от того, какой тип типа вы вкладываете в ArrayList, это объект объекта внутри него.
Общая реализация, основанная на наследовании, принесет две проблемы: первый вопрос о методе GET. Каждый раз, когда мы называем метод GET, мы возвращаем объект объекта, и каждый раз, когда нам приходится поднимать тип нуждающегося в нашем типе, который будет выглядеть очень хлопотно; Второй вопрос о методе добавления. Если мы добавим объект файла в ArrayList, который агрегирует объект String, компилятор не будет генерировать какие -либо запросы на ошибку, что мы не хотим.
Следовательно, начиная с Java 5, ArrayList можно использовать для добавления параметра типа (параметр типа) при его использовании. Этот параметр типа используется для указания типа элемента в ArrayList. Введение параметров типа решает две задачи, упомянутые выше, как показано в следующем коде:
Arraylist <string> s = new ArrayList <string> (); S.Add ("ABC"); String S = S.Get (0); // нет необходимости отбрасывать S.Add (123); // ошибка компиляции, вы можете добавить только строки объектов ...В приведенном выше коде, после того, как компилятор «знает» строку параметров типа ArrayList, он завершит проверку литья и типа для нас.
2. Общие классы
Так называемый общий класс-это класс с одним или несколькими параметрами типа. Например:
Public Class Pare <T, U> {Private T First; Частный U второй; Public Pair (T First, U Second) {this.first = First; это. Second = Second; } public t getFirst () {return First; } public u getSecond () {return Second; } public void setFirst (t newValue) {first = newValue; }}В приведенном выше коде мы видим, что типовые параметры общей пары классов - это T и U, и расположены в угловых скобках после имени класса. Здесь T означает первую букву типа, которая представляет тип. Обычно используются E (элемент), k (ключ), v (значение) и т. Д. Конечно, также очень хорошо не использовать эти буквы для обозначения параметров типа.
При создании общего класса нам нужно только заменить параметр типа определенным типом, таким как создание пары <t, u> класс, мы можем сделать это:
Pair <string, integer> pair = new pair <string, integer> ();
3. Общие методы
Так называемый общий метод-это метод с параметрами типа. Это может быть определено в общем классе или в обычном классе. Например:
открытый класс arrayalg {public static <t> t getMiddle (t [] a) {return a [a.length / 2]; }}Метод getMiddle в приведенном выше коде является общим методом, и определенный формат заключается в том, что переменная типа размещается после модификатора и перед типом возврата. Мы видим, что приведенные выше общие методы могут быть вызваны для различных типов массивов. Когда известно, что типы этих массивов ограничены, хотя они также могут быть реализованы при перегрузке, эффективность кодирования намного ниже. Пример кода для вызова вышеупомянутого общего метода заключается в следующем:
String [] strings = {"aa", "bb", "cc"};
String middle = arrayalg.getmiddle (names);
4. Ограничение переменных типа
В некоторых случаях общие классы или общие методы хотят дополнительно ограничить параметры своего типа. Например, если мы хотим определить параметры типа, которые могут быть только подклассами определенного класса или только классы, которые реализуют определенный интерфейс. Соответствующий синтаксис выглядит следующим образом:
<T Extends BundingType> (BoundingType - это класс или интерфейс). Там может быть более 1 BangingType, просто используйте »и« для подключения ».
5. Понять реализацию дженериков
На самом деле, с точки зрения виртуальных машин, нет концепции «дженериков». Например, общая пара классов, которую мы определили выше, выглядит так в виртуальной машине (то есть после составления в Bytecode):
Public Class Pare {Private Object First; частный объект второй; public pare (Object First, Object Second) {this.first = First; это. Second = Second; } public Object getFirst () {return First; } public Object getSecond () {return Second; } public void setFirst (Object newValue) {first = newValue; } public void setSecond (Object newValue) {second = newValue; }}Приведенный выше класс получается путем стирания типа и является необработанным типом, соответствующим парой общему классу. Тип стирания означает замену всех параметров типа на ограничение (замените его объектом, если ограничения не добавляются).
Мы можем просто проверить, что после составления Pair.java, тип «Javap -c -s pare», чтобы получить:
Линия с «дескриптором» на приведенном выше рисунке является подписью соответствующего метода. Например, из четвертой строки мы видим, что два формальных параметра парного конструктора стали объектом после стирания типа.
Поскольку общая пара классов становится его необработанным типом в виртуальной машине, метод GetFirst возвращает объект объекта, и с точки зрения компилятора этот метод возвращает объект параметра типа, указанный при создании класса. На самом деле, это компилятор, который помогает нам завершить работу кастинга. Другими словами, компилятор преобразует вызов в метод GetFirst в паре общего класса в две инструкции виртуальной машины:
Первый - это призыв к методу необработанного типа GetFirst, который возвращает объект объекта; Вторая инструкция отбрасывает возвращаемый объект объекта в тип типа параметров, который мы указали.
Стирание типа также происходит в общих методах, таких как следующие общие методы:
Public Static <T расширяется сопоставимо> t min (t [] a)
После компиляции это станет таким после стирания типа:
общественный статический сопоставимый мин (сопоставимый [] а)
Тип стирания методов может вызвать некоторые проблемы, рассмотрим следующий код:
Class DateInterval Extends Pair <Дата, дата> {public void setSecond (date second) {if (second.compareto (getFirst ())> = 0) {super.setsecond (second); }} ...}После того, как приведенный выше код стерт по типу, он становится:
класс DateInterval Extends Pair {public void setSecond (дата второй) {...} ...}В классе DateInterval также существует метод SETSECOND, унаследованный от класса пар (после стирания типа) следующим образом:
Public Void SetSecond (объект второй)
Теперь мы видим, что этот метод имеет разные подписи метода (разные формальные параметры) из секундного метода, переопределенного DateInterval, поэтому это два разных метода, однако эти два метода не должны быть разными методами (потому что он переопределяется). Рассмотрим следующий код:
DateInterval Interval = New DateInterval (...); пара <дата, дата> пара = интервал; дата adade = новая дата (...); pair.setsecond (adate);
Из приведенного выше кода мы видим, что пара фактически относится к объекту DateInterval, поэтому следует вызвать метод SETSECOND DateInterval. Проблема здесь в том, что тип стирает конфликты с полиморфизмом.
Давайте рассмотрим, почему возникает эта проблема: пара была ранее объявлена как пара типов <дата, дата>, и этот класс, по -видимому, имеет только один метод «setsecond (объект)» в виртуальной машине. Следовательно, при запуске виртуальная машина обнаруживает, что пара фактически относится к объекту DateInterval, она будет вызвать «setSecond (объект)» DateInterval, но в классе DateInterval есть только метод «setSecond (date)».
Решение этой проблемы состоит в том, чтобы создать метод моста в DateInterval компилятором:
public void setSecond (объект второй) {setSecond ((date) второй);}6. Что следует отметить
(1) Параметры типа не могут быть созданы с основными типами
То есть следующее заявление является незаконным:
Pair <int, int> pare = new pare <int, int> ();
Тем не менее, мы можем использовать соответствующий тип упаковки вместо этого.
(2) не может бросить или захватить общие экземпляры класса
Общее расширение класса, бросается незаконно, поэтому общие случаи класса не могут быть выброшены или захвачены. Но законно использовать параметры типа в объявлениях исключения:
public static <t Extends Throwable> void dowork (t t) бросает t {try {...} catch (throwable RealCause) {t.initcause (RealCause); бросить t; }}(3) Параметризованный массив является незаконным
В Java объект [] массив может быть родительским классом любого массива (потому что любой массив может быть преобразован вверх в массив родительского класса, который определяет тип элемента при определении). Рассмотрим следующий код:
String [] strs = new String [10]; object [] objs = strs; obj [0] = новая дата (...);
В приведенном выше коде мы назначаем элемент массива объекту, который удовлетворяет типу родительского класса (объект), но в отличие от исходного типа (пара), он может пройти во время компиляции, и исключение ArrayStoreException будет брошено во время выполнения.
Основываясь на приведенных выше причинах, предположим, что Java позволяет нам объявить и инициализировать общий массив посредством следующего оператора:
Pair <string, string> [] pairs = new pare <string, string> [10];
Затем после того, как виртуальная машина выполняет стирание типа, пары фактически становятся парой [] массивами, и мы можем преобразовать ее вверх в массив объекта []. В настоящее время, если мы добавим пару <дата, дата> объекты к нему, мы можем пройти проверки времени компиляции и проверки времени выполнения. Наше первоначальное намерение состоит в том, чтобы просто позволить этой паре хранилища массива <строка, строки> объектов, что затрудняет поиск ошибок. Следовательно, Java не позволяет нам объявлять и инициализировать общий массив через форму вышеуказанного оператора.
Общий массив может быть объявлен и инициализирован с использованием следующего оператора:
Pair <string, string> [] pairs = (pair <string, string> []) new pare [10];
(4) Тип переменной не может быть создана
Переменные типа не могут использоваться в таких формах, как «Новый T (...)», «Новый T [...]», «T.Class». Причина, по которой Java запрещает нам это делать, проста. Поскольку существует стирание типа, такие утверждения, как «Новый T (...)», станут «новым объектом (...)», что обычно не то, что мы имеем в виду. Мы можем заменить вызов на «Новый T [...]» со следующим утверждением:
массивы = (t []) новый объект [n];
(5) Переменные типа не могут быть использованы в статическом контексте общих классов
Обратите внимание, что мы подчеркиваем общие классы здесь. Потому что статические общие методы могут быть определены в обычных классах, таких как метод GetMiddle в классе Arrayalg, упомянутом выше. По причинам такого правила, пожалуйста, рассмотрите следующий код:
Public Class People <t> {public static t name; public static t getName () {...}}Мы знаем, что в то же время в памяти может быть более одного экземпляра класса. Предположим, что сейчас есть объект <String> и люди <Integer> в памяти, а статические переменные и статические методы класса используются всеми экземплярами класса. Итак, вопрос в том, является ли тип строки имени или тип целого числа? По этой причине переменные типа не допускаются в Java для использования в статических контекстах общих классов.
7. Тип подстановочного знака
Прежде чем представить тип подстановочного знака, сначала введите два момента:
(1) Предположим, что студент-это подкласс людей, но пара <студент, студент> не является подклассом пары <люди, люди>, и нет "IS-A" отношения между ними.
(2) Существует связь «is-a» между парой <t, t> и ее исходной парой типа. Пара <t, t> может быть преобразована в тип пары в любом случае.
Теперь рассмотрим этот метод:
public static void printname (пара <люди, люди> p) {people p1 = p.getfirst (); System.out.println (p1.getName ()); // Предположим, что класс людей определяет метод экземпляра GetName}В приведенном выше методе мы хотим иметь возможность передавать параметры пары <студента, студента> и пары <люди, люди> в то же время, но нет «is-a» между ними. В этом случае Java предоставляет нам решение: используйте Pare <? расширяет людей> как тип формального параметра. То есть пара <студента, студент> и пары <люди, люди> можно рассматривать как подклассы пары <? расширяет людей>.
Код, который выглядит как «<? Extending BundingType>», называется ограничением подтипа символов подстановочного знака. Соответственно, это супер -тип ограничения символов подстановочного знака, формат заключается в следующем: <? Super BoundingType>.
Теперь давайте рассмотрим следующий код:
Пара <Tlade> Студенты = Новая пара <tlade> (студент1, студент2); пара <? расширяет людей> Wildchards = студенты; Wildchards.SetFirst (People1);
Третья строка приведенного выше кода сообщит об ошибке, потому что Wildchards - это пара <? Распространяет People> Object, и его метод SetFirst и метод GetFirst заключаются в следующем:
void setFirst (? Распространяет людей)? Распространяет людей GetFirst ()
Для метода SetFirst компилятор не будет знать, какой тип формальных параметров (известно, как подкласс людей). Когда мы пытаемся передать объект People, компилятор не может определить, являются ли люди и формальные параметры «is-a», поэтому вызов метода SetFirst сообщит об ошибке. Законно назвать метод GetFirst Wildchards, потому что мы знаем, что он вернет подкласс людей, а подкласс людей «всегда - это люди». (Вы всегда можете преобразовать объекты подкласса в родительские объекты)
В случае ограничения супертипа подстановочного знака метод вызова является незаконным, в то время как метод вызова сеттера является законным.
В дополнение к ограничениям подтипа и ограничениям супертипа есть также подстановочный знак, называемый бесконечным подстановочным знаком, который выглядит так: <?>. Когда мы будем использовать эту вещь? Рассмотрим этот сценарий. Когда мы вызовым метод, мы вернем метод GetPairs, который вернет набор пар <T, t>. Среди них пара <студент, студент> и пара <учитель, учитель> объекты. (Не существует никаких отношений наследования между классом ученика и классом учителя), очевидно, в данном случае как ограничение подтипа, так и ограничение супертипа не могут быть использованы. В настоящее время мы можем использовать это утверждение для его решения:
Пара <?> [] Пары = getPairs (...);