Anatoly Vorobey (avva) wrote,
Anatoly Vorobey
avva

Category:

дженерики (программистское)

(эта запись может быть интересна разве что программистам)

Прочитал Java Generics FAQ. 500 страниц, между прочим, в PDF-версии. 500 страниц. Когда вы в последний раз читали FAQ на 500 страниц? Ну вот и я тогда же. С трудом верится даже теперь, по прочтении. Правда, там много места занимают бесконечные индексы и оглавления и заголовки, так что реально "мяса" страниц на 350. Но и этого довольно.

Теперь я понимаю, зачем в Джаве класс Enum определяется, как Enum<E extends Enum<E>>, что это в точности значит, и зачем это нужно. Не уверен, что мне нравится, что я это теперь понимаю.

Многие объяснения в этом фэке читаются, как один сплошной WTF. Ты понимаешь, в процессе чтения, почему это сделано так, а не иначе. Почему и тут исключение, и тут, и это надо делать через задницу, а то даже через задницу не сделать. Почему - один пример наугад из сотни - все обычные вещи с параметризованными типами можно делать, а вот создавать массивы из них нельзя. Кроме случая, когда они параметризованы неограниченными вопросиками. Ты понимаешь, почему это так получается, но не оставляет ощущение глубокого WTF на тему того, как мучительно и болезненно эти дженерики врастают в плоть языка. Сказать, что это leaky abstraction - ничего не сказать; эта лодка не протекает, она буквально состоит из воды.

Вот один из прекрасных вопросов из этого FAQ:
What is the difference between a Collection<Pair<String,Object>>, a Collection<Pair<String,?>> and a Collection<? extends Pair<String,?>>?
Ответ начинается так: "All three types refer to collections that hold pairs where the first part is a String and the second part is of an arbitrary type. The differences are subtle." Потом на двух страницах объясняются эти subtle differences.

По-моему, внесение дженериков в Джаву было огромной ошибкой. Главная польза от них - строгая типизация коллекций - не оправдывает той огромной цены, которую пришлось заплатить: сложностью языка, читабельностью и понимабельностью кода. Учитывая то, что динамическая безопасность у коллекций и так была, необходимость безопасности на уровне компиляции была кем-то, по-видимому, сильно преувеличена. Да, конечно, намного удобнее и проще написать ArrayList<String>, и знать, что попытка всунуть туда что-то другое вообще не скомпилируется. Это удобнее, чем всунуть что-то не то в ArrayList и получить исключение в рантайме, когда это взяли и попытались привести к String. Баги ловятся быстрее. Но насколько быстрее, как часто на практике это оказывалось существенным, и оправдывает ли эта польза введение архитектуры, приводящей к Enum<E extends Enum<E>> или вопросам, подобным процитированному выше?

Одним из главных преимуществ Джавы по сравнению с C++ было именно grokability исходного кода средним программистом: посмотрев на исходник какого-то класса, который он раньше не видел, средний программист на Джаве - не гуру, не хватающий звезды с неба итд. - мог сказать, что делает каждая строка и зачем это нужно. Дженерики это свойство языка с помпой похоронили. Кто были люди, которые приняли решение так поступить, и радовались ли они красивому трюку само-референтных типов? И это только ущерб понятности кода, не говоря о всем этом огромном числе исключений и плохой совместимости с существующими частями языка - массивами, рефлекцией, исключениями, итд. итд. итд.

Можно ли было добиться той же пользы, что дали дженерики, менее разрушительными способами? Пусть не той же, но главной ее части, мне кажется, можно было. Скажем, какая-нибудь аннотация в момент создания коллекции, подсказывающая компилятору, что там должно быть. Просто сказать: в эту коллекцию я хочу класть строки, или такие-то пары, итп. И пусть компилятор проверяет, что сможет, и вставляет эксплицитное приведение к строкам или парам, когда мы из коллекции что-то достаем. И все. Без extends, без wildcards, для всех мест, где это действительно надо, пусть программист продолжает приводить эксплицитно. Конечно, такая аннотация не покрыла бы все случаи, но самые простые и важные, думаю, покрыла бы. Главное ведь то, что у дженериков почти нулевой эффект на рантайм, поэтому необязательно было вносить их глубоко в ткань языка, so to speak; подсказки компилятору достигают схожей цели. Простая (простая!) и удобная подсказка захватила бы, мне кажется, большинство багов с типами в коллекцях, которые до дженериков проявлялись только в рантайме.

(disclaimer: я редко и мало пишу на Джаве, и не эксперт в ней)
Subscribe
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 162 comments
Previous
← Ctrl ← Alt
Next
Ctrl → Alt →
Previous
← Ctrl ← Alt
Next
Ctrl → Alt →