Обобщённые классы
© |
NetCracker Technology Corp. |
/ |
Применение обобщённых классов
.
Область применения параметризованных классов огромна:
. конечно, коллекции и вообще любого рода контейнеры:
List<Integer>, Set<Long>, Map<String, Runnable>,
Future<HttpResponse>, Optional<Model>;
. компараторы:
public interface Comparator<T> { int compare(T left, T right);
}
. объекты-функции:
public interface Function<F, T> { T apply(F arg);
}
public interface Callable<T> { T call() throws Exception;
}
.
© |
NetCracker Technology Corp. |
/ |
Применение обобщённых классов
.
. разного рода провайдеры:
public interface Provider<T> { T get();
}
public interface ThrowingProvider<T, E extends Throwable> { T get() throws E;
}
. обобщённые DAO:
public abstract class GenericDAO<T, ID> { protected GenericDAO(Class<T> clazz) { ... } public T findById(ID id) { ... }
}
. конвертеры: |
|
public interface StringConverter<T> { |
|
T fromString(String s); |
|
String toString(T obj); |
|
} |
|
. |
...тысячи их. |
© NetCracker Technology Corp. |
/ |
Синтаксис
.
Синтаксис объявления класса с ти́повыми параметрами:
public class SomeClass<T , T , ...> {
...
}
Внутри тела класса типовые параметры могут использоваться как обычные имена классов (за некоторым исключением).
Ясно, что параметризованными могут быть также и интерфейсы и абстрактные классы.
.
© |
NetCracker Technology Corp. |
/ |
Наследование
.
От обобщённых классов/интерфейсов можно наследоваться:
public interface Function<F, T> { T apply(F arg);
}
public class Uppercaser extends Function<String, String> { @Override public String apply(String arg) {
return arg.toUpperCase();
}
}
Наследующий класс также может быть обобщённым:
public abstract class Stringer<T> extends Function<T, String> { @Override public String apply(T arg);
}
.
© |
NetCracker Technology Corp. |
/ |
Верхние границы
.
Проблема: мы ничего не можем делать с переменными, типом которых параметризуется класс (кроме операций над Object):
public class OperationsWrapper<T> { private final T inner;
public OperationsWrapper(T inner) { this.inner = inner; } public double doubleSquare() {
// Какой метод вызвать? double value = inner.???(); return value*value;
}
public int truncateAndAdd(T other) { int truncated = inner.???(); return truncated + other.???();
}
}
Доступная информация — inner является Object’ом, поэтому мы можем вызывать на нём только методы класса Object.
.
© |
NetCracker Technology Corp. |
/ |
Верхние границы
.
Решение: верхние границы для типовых параметров:
public class OperationsWrapper<T extends Number> { private final T inner;
public OperationsWrapper(T inner) { this.inner = inner; } public double doubleSquare() {
//Работает, так как T является подклассом Number,
//у которого есть метод doubleValue()
double value = inner.doubleValue(); return value*value;
}
public int truncateAndAdd(T other) {
// Аналогично
int truncated = inner.intValue(); return truncated + other.intValue();
}
}
На переменных с таким «ограниченным» типом мы можем вызывать соответствующие методы.
.
© |
NetCracker Technology Corp. |
/ |
Больше верхних границ
.
В верхней границе можно также указывать несколько типов:
public class OnceAppender<T extends Appendable & AutoCloseable> { private final T inner;
public OnceAppender(T inner) { this.inner = inner; }
public void appendAndClose(String s) throws Exception {
//Метод append() объявлен в Appendable,
//метод close() --- в AutoCloseable inner.append(s);
inner.close();
}
}
.
© |
NetCracker Technology Corp. |
/ |
Сложные верхние границы
.
В верхней границе можно использовать типы, параметризованные ограничиваемым типом:
public interface Comparable<T> { int compareTo(T other);
}
public class TrivialComparator<T extends Comparable<T>> extends Comparator<T> {
private final T inner;
public TrivialComparator(T inner) { this.inner = inner; } @Override public int compare(T left, T right) {
return left.compareTo(right);
}
}
Такое нужно в случае, если обобщённый тип (в данном случае Comparable<T>) должен использовать объекты собственного типа.
.
© |
NetCracker Technology Corp. |
/ |
Примитивные типы
.
В качестве типовых параметров нельзя использовать примитивные типы:
List<int> list = new ArrayList<>(); // Ошибка компиляции
Для списка чисел и прочего нужно использовать обёртки:
List<Integer> list = new ArrayList<>();
Страдает производительность; есть библиотеки производительных коллекций примитивных типов (Trove).
.
© |
NetCracker Technology Corp. |
/ |