Java. Generics. Wildcards



1. Зачем нужны дженерики 

Дженерики появились в Java в версии 1.5, для того, чтобы исключить неверную типизацию.

Пример, до Java 1.5, были raw types  (сырые типы) . В данном примере objects - это raw type:


List objects = new ArrayList();
objects.add("1");
objects.add("2");
objects.add("3");
objects.add("4");
objects.add("5");
String s0 = (String) objects.get(0);
String s1 = (String) objects.get(1);
String s2 = (String) objects.get(2);
String s3 = (String) objects.get(3);
String s4 = (String) objects.get(4);
Тут все работает верно и ошибок нет, т.к. мы в массив List 
добавляем String.
Но что будет если мы ошибемся и добавим не строку "4", а число 4:
List objects = new ArrayList();
objects.add("1");
objects.add("2");
objects.add("3");
objects.add(4); //тут наша ошибка
objects.add("5");
String s0 = (String) objects.get(0);
String s1 = (String) objects.get(1);
String s2 = (String) objects.get(2);
String s3 = (String) objects.get(3);
String s4 = (String) objects.get(4);

Мы получим в рантайме ошибку:
Exception in thread "main" java.lang.ClassCastException: 
java.lang.Integer cannot be cast to java.lang.String
Конечно, нам хотелось бы, чтобы данная ошибка была на этапе компиляции, а не в рантайме.
Поэтому нам нужно использовать Дженерики:
List<String> objects = new ArrayList();
Благодаря дженерикам:
а)мы избавились от ошибки ClassCastException:
objects.add(4);
б) Убрали приведение типов (String) и код стал выглядеть проще:
Наш код будет выглядеть так:
List<String> objects = new ArrayList();
objects.add("1");
objects.add("2");
objects.add("3");
objects.add("4");
objects.add("5");
String s0 = objects.get(0);
String s1 = objects.get(1);
String s2 = objects.get(2);
String s3 = objects.get(3);
String s4 = objects.get(4);

2.Основная проблема с коллекциями. Знакомство с wildcard

Класс Parent:

public class Parent {
private String name;

public Parent(String name) {
this.name = name;
}

public String getName() {
return name;
}
}
Ну и наследник Child:
public class Child extends Parent {
public Child(String name) {
super(name);
}
}
Сделаем какой-нибудь простенький метод для печати:
static void print(List<Parent> par) {
for (Parent p : par) {
System.out.println(p.getName());
}
}
И попробуем распечатать коллекцию Parents
List<Parent> parents = new ArrayList<>();
parents.add(new Parent("parent 1"));
parents.add(new Parent("parent 2"));
parents.add(new Parent("parent 3"));
print(parents);
То ошибок тут не будет :
name 1
name 2
name 3
Но !!! Если мы попробуем распечатать список детей, то получим ошибку:

List<Child> children = new ArrayList<>();
children.add(new Child("child 1"));
children.add(new Child("child 2"));
children.add(new Child("child 3"));
print(children);
Что List<Child> НЕ наследуется от List<Parent>. Те Класс Child 
наследуется от Класса Parent, но List<Child> 
НЕ наследуется от List<Parent>. 
List<Child> наследуется от Collection<Child>, а Collection<Child> 
наследуется от Iterable<Child>, а потом идет класс Object. 
Соответственно, у List<Child> и List<Parent> общий родитель Object.
Соответственно используется wildcard или по-другому Joker - ?.

static void print(List<? extends Parent> par) {
for (Parent p : par) {
System.out.println(p.getName());
}
}

List<? extends Parent> - данная запись означает, что в List можно
передавать любых потомков класса Parent и сам класс Parent.






Комментарии

Популярные сообщения из этого блога

Java. Лучшая практика работы с Enum

Java. Разбор класса Collections

Java. Основы FunctionalInterface