Обобщения в Java облегчают параметрический полиморфизм . С помощью параметров типа вы можете передавать аргументы типам. Так же, как метод, подобный String foo(String s)
, моделирует некоторое поведение, не только для конкретной строки, но и для любой строки s
, так и тип, подобный List<T>
, моделирует некоторое поведение, не только для определенного типа, но для любой тип . List<T>
говорит, что для любого типа T
, существует List
из T
s . Так что List
на самом деле является конструктором типа . Он принимает тип в качестве аргумента и создает другой тип в качестве результата.
Вот пара примеров универсальных типов, которые я использую каждый день. Во-первых, очень полезный универсальный интерфейс:
public interface F<A, B> {
public B f(A a);
}
Этот интерфейс говорит, что для любых двух типов, A
и B
, есть функция (называемая f
), которая принимает A
и возвращает B
. Когда вы При реализации этого интерфейса A
и B
могут быть любых типов, которые вы хотите, при условии, что вы предоставляете функцию f
, которая берет первое и возвращает второе. Вот пример реализации интерфейса:
F<Integer, String> intToString = new F<Integer, String>() {
public String f(int i) {
return String.valueOf(i);
}
}
До появления дженериков полиморфизм достигался с помощью подкласса с использованием ключевого слова extends
. С помощью дженериков мы можем фактически покончить с подклассами и использовать вместо этого параметрический полиморфизм. Например, рассмотрим параметризованный (универсальный) класс, используемый для вычисления хеш-кодов для любого типа. Вместо переопределения Object.hashCode () мы бы использовали общий класс, подобный этому:
public final class Hash<A> {
private final F<A, Integer> hashFunction;
public Hash(final F<A, Integer> f) {
this.hashFunction = f;
}
public int hash(A a) {
return hashFunction.f(a);
}
}
Это гораздо более гибко, чем использование наследования, потому что мы можем остаться с темой использования композиции и параметрического полиморфизма без блокировки хрупких иерархий.
Обобщения Java не идеальны, хотя. Вы можете абстрагироваться от типов, но вы не можете абстрагироваться от конструкторов типов, например. То есть вы можете сказать «для любого типа T», но нельзя сказать «для любого типа T, который принимает параметр типа A».
Я написал статью об этих пределах обобщений Java, здесь.
Источник для множества полезных универсальных типов можно найти здесь. . Также проверьте исходный код стандартной библиотеки Java, если вы этого еще не сделали.