Проклятие дженериков 💀
Дженерики могут показаться очень простой темой.
Например, вот так в Java выглядят классные и простые методы интерфейса List
:
interface List<E> extends Collection<E> {
boolean add(E e);
E set(int index, E element);
}
Но у обобщений много нюансов: вложенность, вариантность, границы и т.д. Это сильно усложняет их использование.
Вот не менее классный, но совсем непростой flatMap
интерфейса Stream
🙈:
interface Stream<T> extends BaseStream<T, Stream<T>> {
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
}
Также, реализация дженериков - всегда трейдоф. Мы либо получаем большой исполняемый файл, из-за того, что приходится генерировать код для разных типов. Либо получаем дополнительную нагрузку в рантайме, из-за различных проверок.
Из-за таких сложностей, в языке Go (философия которого - простота и минимализм) дженерики появились аж через 12 лет после релиза языка. А первый коммент про то что нужны дженерики появился меньше чем через 24 часа🙃
Во многих популярных языках дженерики появились не с первой версии, но рано или поздно, разработчики были вынуждены их ввести:
С++ вышел в 1979, дженерики - 1986
Java - 1996, дженерики - 2004
C# - 2001, дженерики - 2005
Go - 2009, дженерики - 2021