Руководство Java Generics
1. Почему Java Generics?
Generics это понятие, введенное в Java, начиная с версии 5. Перед тем, как ознакомить вас с понятием Generics, посмотрим на код Java перед версией 5.
В данном примере, ArrayList это списко, вы можете добавить., удалить, изменить в списке, и иметь доступ к элементам в списке.
BeforeJ5Example.java
package org.o7planning.tutorial.generics;
import java.util.ArrayList;
public class BeforeJ5Example {
public static void main(String[] args) {
// Создать объект ArrayList (Список).
// Чтобы содержать имена пользователей.
ArrayList userNames = new ArrayList();
// Добавить String в список.
userNames.add("tom");
userNames.add("jerry");
// Вы случайно добавляете элемент не String вида в список.
// (Это вполне позволено).
userNames.add(new Integer(100));
// И получить первый элемент
// Он является Object (Но вы знаете, что он является String)
// ==> tom
Object obj1 = userNames.get(0);
// Сделать cast в String.
String userName1 = (String) obj1;
System.out.println("userName1 = " + userName1);
// Получить элемент 2.
// (Вы знаете, что он является String)
// ==> jerry
String userName2 = (String) userNames.get(1);
System.out.println("userName2 = " + userName2);
// Получить 3-ий элемент и сделать cast чтобы он стал String.
// (На самом деле он является Integer).
// (Ошибка cast происходит здесь).
String userName3 = (String) userNames.get(2);
System.out.println("userName3 = " + userName3);
}
}
Ситуация в Java перед версией 5:
Вам нужно создать объект ArrayList с целью хранения только элементов вида String, при этом добавить в этот список элемент не вида String где-нибудь в программе (Это вполне возможно), когда вы получаете эти элементы и приводите к виду String, выбрасывается исключение.
Вам нужно создать объект ArrayList с целью хранения только элементов вида String, при этом добавить в этот список элемент не вида String где-нибудь в программе (Это вполне возможно), когда вы получаете эти элементы и приводите к виду String, выбрасывается исключение.
- TODO (Image)
Java 5 вводит понятие Generics. С помощью Generics, вы можете создать объект ArrayList который позволяет содержать только объекты вида String, и не позволяет иметь другие виды объектов.
J5Example.java
package org.o7planning.tutorial.generics;
import java.util.ArrayList;
public class J5Example {
public static void main(String[] args) {
// Создать ArrayList (Список)
// Этот список только позволяет содержать элементы вида String.
ArrayList<String> userNames = new ArrayList<String>();
// Добавить String в список.
userNames.add("tom");
userNames.add("jerry");
// Вы не можете добавить элементы не String вида в список.
// (Ошибка при компиляции).
userNames.add(new Integer(100)); // Compile Error!
// Вам не нужно делать cast элемента.
String userName1 = userNames.get(0);
System.out.println("userName1 = " + userName1);
}
}
Когда вы создаете объект ArrayList<String>, он содержить только элементы вида String, компилятор Java не позволяет этому объекту содержать элементы отличающиеся от String.
2. ВидGeneric для Class & Interface
Generics Class
Пример ниже определяет class generics. KeyValueэто class generics который содержит пару ключей/значения (key/value).
KeyValue.java
package org.o7planning.tutorial.generics.ci;
public class KeyValue<K, V> {
private K key;
private V value;
public KeyValue(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
K, V в class KeyValue<K,V> называется параметорм generics, который является определенной ссылкой. При использовании этого class-а вам нужно определить точный параметр.
Смотрите пример использования class KeyValue.
Смотрите пример использования class KeyValue.
KeyValueDemo.java
package org.o7planning.tutorial.generics.ci;
public class KeyValueDemo {
public static void main(String[] args) {
// Создать объект KeyValue
// Integer: Номер телефона (K = Integer)
// String: Имя пользователя. (V = String).
KeyValue<Integer, String> entry = new KeyValue<Integer, String>(12000111, "Tom");
// Java понимает вид возврата как Integer (K = Integer).
Integer phone = entry.getKey();
// Java понимает вид возврата как String (V = String).
String name = entry.getValue();
System.out.println("Phone = " + phone + " / name = " + name);
}
}
Запуск примера:
Phone = 12000111 / name = Tom
Наследование класса Generics
Сlass расширенный из class generics, может определить вид параметра generics, сохранить параметры generics или добавить параметры generics.
Пример 1:
PhoneNameEntry.java
package org.o7planning.tutorial.generics.ci;
// Этот класс расширенный (extends) из класса KeyValue<K,V>.
// И ясно определяет K,V:
// K = Integer (Номер телефона).
// V = String (Имя пользователя).
public class PhoneNameEntry extends KeyValue<Integer, String> {
public PhoneNameEntry(Integer key, String value) {
super(key, value);
}
}
Пример использования PhoneNameEntry:
PhoneNameEntryDemo.java
package org.o7planning.tutorial.generics.ci;
public class PhoneNameEntryDemo {
public static void main(String[] args) {
PhoneNameEntry entry = new PhoneNameEntry(12000111, "Tom");
// Java понимает вид возврата как Integer.
Integer phone = entry.getKey();
// Java понимает вид возврата как String.
String name = entry.getValue();
System.out.println("Phone = " + phone + " / name = " + name);
}
}
Пример 2:
StringAndValueEntry.java
package org.o7planning.tutorial.generics.ci;
// Этот класс расширен (extends) из класса KeyValue<K,V>.
// Ясно определить вид параметра <K> как String.
// Сохранить вид параметра Generic <V>.
public class StringAndValueEntry<V> extends KeyValue<String, V> {
public StringAndValueEntry(String key, V value) {
super(key, value);
}
}
Пример использования StringAndValueEntry class:
StringAndValueEntryDemo.java
package org.o7planning.tutorial.generics.ci;
public class StringAndValueEntryDemo {
public static void main(String[] args) {
// (Код сотрудника, Имя сотрудника).
// V = String (Имя сотрудника)
StringAndValueEntry<String> entry = new StringAndValueEntry<String>("E001", "Tom");
String empNumber = entry.getKey();
String empName = entry.getValue();
System.out.println("Emp Number = " + empNumber);
System.out.println("Emp Name = " + empName);
}
}
Пример 3:
KeyValueInfo.java
package org.o7planning.tutorial.generics.ci;
// Этот класс расширен (extends) из класса KeyValue<K,V>
// Он имеет еще один параметр Generics <I>.
public class KeyValueInfo<K, V, I> extends KeyValue<K, V> {
private I info;
public KeyValueInfo(K key, V value) {
super(key, value);
}
public KeyValueInfo(K key, V value, I info) {
super(key, value);
this.info = info;
}
public I getInfo() {
return info;
}
public void setInfo(I info) {
this.info = info;
}
}
Generics Interface
Интерфейс с параметром Generics:
GenericInterface.java
package org.o7planning.tutorial.generics.ci;
public interface GenericInterface<G> {
public G doSomething();
}
Пример class-а, применяющего интерфейс:
GenericInterfaceImpl.java
package org.o7planning.tutorial.generics.ci;
public class GenericInterfaceImpl<G> implements GenericInterface<G>{
private G something;
@Override
public G doSomething() {
return something;
}
}
Java не поддерживает Generics Throwable
Вы не можете создать class generic который является наследником Throwable, java не поддерживет создание такого class-а.
Сообщение ошибки компилятора:
- The generic class MyException<E> may not subclass java.lang.Throwable
Java не поддерживает создание class-а Throwable generic, так как это не приносит никакую пользу. Причиной является информация Generic только используется компилятором для управления кодом программиста. В процессе запуска Java информация Generic не существует, объект Mistake<Account> или Mistake<User> являются объектом Mistake.
} catch( Mistake<Account> ea) {
// Если происходит исключение Mistake, данный блок будет выполнен.
...
} catch( Mistake<User> eu) {
// Данный блок никогда не выполняется
...
}
3. Методы generics
Метод в class или интерфейсе, который может стать generic (generify).
MyUtils.java
package org.o7planning.tutorial.generics.m;
import java.util.ArrayList;
import org.o7planning.tutorial.generics.ci.KeyValue;
public class MyUtils {
// <K,V> : Говорит этот метод имеет 2 вида параметра K,V
// Метод возвращает объект вида K.
public static <K, V> K getKey(KeyValue<K, V> entry) {
K key = entry.getKey();
return key;
}
// <K,V> : Говорит этот метод имеет 2 вида параметра K,V
// Метод возвращает объект вида V.
public static <K, V> V getValue(KeyValue<K, V> entry) {
V value = entry.getValue();
return value;
}
// ArrayList<E>: Список содержит элемент вида E.
// Метод возвращает объект вида E.
public static <E> E getFirstElement(ArrayList<E> list) {
if (list == null || list.isEmpty()) {
return null;
}
E first = list.get(0);
return first;
}
}
Например, используя метод generics:
MyUtilsDemo.java
package org.o7planning.tutorial.generics.m;
import java.util.ArrayList;
import org.o7planning.tutorial.generics.ci.KeyValue;
public class MyUtilsDemo {
public static void main(String[] args) {
// K = Integer: Phone
// V = String: Name
KeyValue<Integer, String> entry1 = new KeyValue<Integer, String>(12000111, "Tom");
KeyValue<Integer, String> entry2 = new KeyValue<Integer, String>(12000112, "Jerry");
// (K = Integer).
Integer phone = MyUtils.getKey(entry1);
System.out.println("Phone = " + phone);
// Список содержит элементы вида KeyValue<Integer,String>.
ArrayList<KeyValue<Integer, String>> list = new ArrayList<KeyValue<Integer, String>>();
// Добавить элемент в список.
list.add(entry1);
list.add(entry2);
KeyValue<Integer, String> firstEntry = MyUtils.getFirstElement(list);
System.out.println("Value = " + firstEntry.getValue());
}
}
4. Инициализация объекта Generic
Иногда вы хотите инициировать объект Generic:
// Создать объект Generic.
T t = new T(); // Error
Инициализация объекта generic, как выше не позволено, так как <T> не существует в момент запуска Java. Он имеет значение только для компилятора, управляющего кодом программиста. Все виды <T> похожи друг на друга и понимаются как Object в момент запуска Java.
Для инициализации объекта generic <T> вам нужно предоставить Java объект Class<T>, Java создаст объект <T> в момент запуска используя Java Reflection.
Для инициализации объекта generic <T> вам нужно предоставить Java объект Class<T>, Java создаст объект <T> в момент запуска используя Java Reflection.
Bar.java
package org.o7planning.tutorial.generics.o;
import java.util.Date;
public class Bar {
// Этот класс должен иметь Constructor (конструктор) по умолчанию .
public Bar() {
}
public void currentDate() {
System.out.println("Now is: " + new Date());
}
}
MyGeneric.java
package org.o7planning.tutorial.generics.o;
public class MyGeneric<T> {
private T tobject;
public MyGeneric(Class<T> tclass)
throws InstantiationException, IllegalAccessException {
this.tobject = (T) tclass.newInstance();
}
public T getTObject() {
return this.tobject;
}
}
MyGenericDemo.java
package org.o7planning.tutorial.generics.o;
public class MyGenericDemo {
public static void main(String[] args) throws Exception {
MyGeneric<Bar> mg = new MyGeneric<Bar>(Bar.class);
Bar bar = mg.getTObject();
bar.currentDate();
}
}
5. Массив Generic
Вы можете объявить массив generic, но вы не можете создать массив generic.
// Вы можете объявить массив generic.
T[] myarray;
// Но не можете инициализировать массив generic.
// (Это не разрешено).
T[] myarray = new T[5]; // Error!
Пример:
GenericArray.java
package org.o7planning.tutorial.generics.a;
public class GenericArray<T> {
private T[] array;
// Contructor.
public GenericArray(T[] array) {
this.array = array;
}
public T[] getArray() {
return array;
}
// Возвращает последний элемент массива.
public T getLastElement() {
if (this.array == null || this.array.length == 0) {
return null;
}
return this.array[this.array.length - 1];
}
}
GenericArrayDemo.java
package org.o7planning.tutorial.generics.a;
public class GenericArrayDemo {
public static void main(String[] args) {
// Массив String.
String[] names = new String[] { "Tom", "Jerry" };
GenericArray<String> gArray = new GenericArray<String>(names);
String last = gArray.getLastElement();
System.out.println("Last Element = " + last);
}
}
Возвращаясь к вопросу, почему Java не поддерживает инициализацию массива Generic:
// Почему Java не поддерживает инициализировать массив Generic?
T[] genericArray = new T[10]; // Error!
Это потому что вид generic не существует в момент запуска, List<String> или List<Integer> это List. Generic только работает с компиляторами для управления кодом программиста. Это означает, что компилятору Java нужно знать точно, что такое <T> для компиляции (compile) new T[10];. Если не знает точно, то по умолчанию считает T как Object. Тогда:
// Допустим Java позволяет инициализировать массив Generic:
T[] tarray = new T[10];
// Во время компиляции (Compile-time)
// компилятор будет считать <T> как Object.
// Команда выше индентична:
T[] tarray = new Object[10];
// Если во время запуска приложения, вы определяете <T> как String.
// Значит:
String[] tarray = new Object[10];
// Пункт выше не разрешен. Причина:
// Type mismatch: cannot convert from Object[] to String[]
Если вы хотите инициализировать массив Generic вам нужно передать Java объект Class<T>, помогая Java создать массив generic в момент запуска используя Java Reflection. Смотрите изображенный пример:
GArray.java
package org.o7planning.tutorial.generics.a;
import java.lang.reflect.Array;
public class GArray<T> {
private Class<T> tclass;
private T[] myArray;
public GArray(Class<T> tclass) {
this.tclass = tclass;
final int size = 10;
myArray = (T[]) Array.newInstance(tclass, size);
}
public T[] getMyArray() {
return this.myArray;
}
}
GArrayDemo.java
package org.o7planning.tutorial.generics.a;
public class GArrayDemo {
public static void main(String[] args) {
GArray<Integer> garray = new GArray<Integer>(Integer.class);
Integer[] myArray = garray.getMyArray();
myArray[0] = 1;
myArray[2] = 0;
}
}
6. Generics с Wildcards
В коде Generic, знак вопроса (?), называется wildcard, который представляет неопределенный вид. Вид параметра wildcard (wildcard parameterized type) это случай вида Generic, там где минимум один параметр является wildcard.
Пример вида параметра wildcard (wildcard parameterized):
- Collection<?>
- List<? extends Number>
- Comparator<? super String>
- Pair<String,?>.
Wildcard могут использованы в разныъ ситуациях: как вид параметра, поле (field), или локальная переменная; иногда как возвратный вид (Будет объяснено в практических примерах). Wildcard никогда не использованы как аргумент для вызова метода Generic, создания объекта class generic, или супертип (supertype).
Wildcard находящиеся в разных позициях имеют разные значения:
- Collection<?> denotes all instantiations of the Collection interface regardless of the type argument.
- List<? extends Number> denotes all list types where the element type is a subtype of Number.
- Comparator<? super String> denotes all instantiations of the Comparator interface for type argument types that are supertypes of String.
Wildcard вида параметра не является конкретным видом, который может появиться в операторе new. Он только является подсказкой применяющегося правила, с помощью generics java, что какой вид имеет действие в определенном случае, где wildcard был использован.
Пример:
Collection<?> coll = new ArrayList<String>();
// Набор содержит только вид Number или подвид Number
List<? extends Number> list = new ArrayList<Long>();
// Объект с подстановочным видом параметра.
// (A wildcard parameterized type)
Pair<String,?> pair = new Pair<String,Integer>();
Некоторые недействительные объявления.
// String не является подвидом Number, поэтому ошибка.
List<? extends Number> list = new ArrayList<String>();
// String не является родительским видом Integer, поэтому ошибка.
ArrayList<? super String> cmp = new ArrayList<Integer>();
Examples with wildcard
WildCardExample1.java
package org.o7planning.tutorial.generics.w;
import java.util.ArrayList;
public class WildCardExample1 {
public static void main(String[] args) {
// Список содержащий элементы вида String.
ArrayList<String> listString = new ArrayList<String>();
listString.add("Tom");
listString.add("Jerry");
// Список содержащий элементы вида Integer
ArrayList<Integer> listInteger = new ArrayList<Integer>();
listInteger.add(100);
// Вы не можете объявить:
ArrayList<Object> list1 = listString; // ==> Error!
// Объект с подстановочным видом параметра.
// (wildcard parameterized object).
ArrayList<? extends Object> list2;
// Вы можете объявить:
//
list2 = listString;
// Или
list2 = listInteger;
}
}
WildCardExample2.java
package org.o7planning.tutorial.generics.w;
import java.util.ArrayList;
import java.util.List;
public class WildCardExample2 {
public static void printElement(List<?> list) {
for (Object e : list) {
System.out.println(e);
}
}
public static void main(String[] args) {
List<String> names = new ArrayList<String>();
names.add("Tom");
names.add("Jerry");
names.add("Donald");
List<Integer> values = new ArrayList<Integer>();
values.add(100);
values.add(120);
System.out.println("--- Names --");
printElement(names);
System.out.println("-- Values --");
printElement(values);
}
}
Тип wildcard parameterized не может использовать методы Generics
ValidWildcard1.java
package org.o7planning.tutorial.generics.w;
import java.util.ArrayList;
public class ValidWildcard1 {
public static void main(String[] args) {
// Список содержащий элементы вида String.
ArrayList<String> listString = new ArrayList<String>();
// Использовать метод generic: add(E).
// Добавить элемент не null в список
listString.add("Tom");
listString.add("Jerry");
// Добавить элемент null в список.
listString.add(null);
}
}
InvalidWildcard1.java
package org.o7planning.tutorial.generics.w;
import java.util.ArrayList;
public class InvalidWildcard1 {
public static void main(String[] args) {
// Список с подстановочным видом параметра.
// (wildcard parameterized type).
ArrayList<? extends Object> listWildcard = listString;
// Вы не можете использовать метод add(E)
// с параметром отличающимся от null.
listWildcard.add("Tom"); // ==> Error!
listWildcard.add("Jerry"); // ==> Error!
// Добавить элемент null в список.
listWildcard.add(null);
}
}
Wildcard не может участвовать в new операторе
Wildcard вида параметра(wildcard parameterized type) не является конкретным видом, и не может появиться в операторе new.
// Параметр Wildcard не может участвовать в операторе new.
List<? extends Object> list= new ArrayList<? extends Object>();
Java Basic
- Настройте java compiler для обработки вашего Annotation (Annotation Processing Tool)
- Программирование на Java для группы с помощью Eclipse и SVN
- Руководство Java WeakReference
- Руководство Java PhantomReference
- Сжатие и декомпрессия в Java
- Настройка Eclipse для использования JDK вместо JRE
- Методы String.format() и printf() в Java
- Синтаксис и новые функции в Java 8
- Регулярные выражения Java
- Руководство Java Multithreading Programming
- Библиотеки Java JDBC Driver для различных типов баз данных
- Руководство Java JDBC
- Получить значения столбцов, автоматически возрастающих при вставлении (Insert) записи, используя JDBC
- Руководство Java Stream
- Руководство Java Functional Interface
- Введение в Raspberry Pi
- Руководство Java Predicate
- Абстрактный класс и Interface в Java
- Модификатор доступа (Access modifiers) в Java
- Руководство Java Enum
- Руководство Java Annotation
- Сравнение и Сортировка в Java
- Руководство Java String, StringBuffer и StringBuilder
- Обработка исключений Java - Java Exception Handling
- Руководство Java Generics
- Манипулирование файлами и каталогами в Java
- Руководство Java BiPredicate
- Руководство Java Consumer
- Руководство Java BiConsumer
- Что мне нужно для начала работы с Java?
- История Java и разница между Oracle JDK и OpenJDK
- Установить Java в Windows
- Установите Java в Ubuntu
- Установите OpenJDK в Ubuntu
- Установить Eclipse
- Установите Eclipse в Ubuntu
- Быстрое изучение Java для начинающих
- История бит и байтов в информатике
- Типы данных в java
- Битовые операции
- Команда if else в Java
- команды switch в Java
- Циклы в Java
- Массивы (Array) в Java
- JDK Javadoc в формате CHM
- Наследование и полиморфизм в Java
- Руководство Java Function
- Руководство Java BiFunction
- Пример Java encoding и decoding с использованием Apache Base64
- Руководство Java Reflection
- Java Удаленный вызов методов - Java RMI
- Руководство Программирование Java Socket
- Какую платформу я должен выбрать для разработки приложений Java Desktop?
- Руководство Java Commons IO
- Руководство Java Commons Email
- Руководство Java Commons Logging
- Понимание Java System.identityHashCode, Object.hashCode и Object.equals
- Руководство Java SoftReference
- Руководство Java Supplier
- Аспектно-ориентированное программирование Java с помощью AspectJ (AOP)
Show More
- Руководства Java Servlet/JSP
- Руководства Java Collections Framework
- Java API для HTML, XML
- Руководства Java IO
- Руководства Java Date Time
- Руководства Spring Boot
- Руководства Maven
- Руководства Gradle
- Руководства Java Web Services
- Руководства Java SWT
- Руководства JavaFX
- Руководства Oracle Java ADF
- Руководства Struts2 Framework
- Руководства Spring Cloud