Я несколько раз видел на этом сайте сообщения, которые осуждают реализацию обобщений в Java. Теперь я могу честно сказать, что у меня не было проблем с их использованием. Тем не менее, я не пытался сделать общий класс самостоятельно. Итак, какие у вас проблемы с общей поддержкой Java?
49
Ответы:
Универсальная реализация Java использует стирание типов . Это означает, что ваши строго типизированные универсальные коллекции действительно имеют тип
Object
во время выполнения. Это имеет некоторые соображения производительности, поскольку это означает, что примитивные типы должны быть помещены в коробку при добавлении в универсальную коллекцию. Конечно, преимущества правильности типа времени компиляции перевешивают общую глупость удаления типа и навязчивую ориентацию на обратную совместимость.источник
TypeLiteral
google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/…new ArrayList<String>.getClass() == new ArrayList<Integer>.getClass()
template<T> T add(T v1, T v2) { return v1->add(v2); }
и там у вас есть действительно общий способ создания функции, которая состоит изadd
двух вещей, независимо от того, что это за вещи, им просто нужно иметь метод с именемadd()
с одним параметром.Add
пример, который я привел, невозможно знать заранее, к каким классам он может быть применен, что является недостатком.Обратите внимание, что ответы, которые уже были предоставлены, сконцентрированы на сочетании языка Java, JVM и библиотеки классов Java.
Нет ничего плохого в обобщениях Java в том, что касается языка Java. Как описано в C # против обобщений Java, обобщение Java в значительной степени хорошо работает на уровне языка 1 .
Неоптимальным является то, что JVM не поддерживает дженерики напрямую, что имеет несколько основных последствий:
Я полагаю, что различие, которое я делаю, является педантичным, поскольку язык Java повсеместно скомпилирован с JVM в качестве цели и библиотекой классов Java в качестве основной библиотеки.
1 С возможным исключением подстановочных знаков, что, как полагают, делает вывод типа неразрешимым в общем случае. Это основное различие между обобщениями C # и Java, которое вообще не упоминается очень часто. Спасибо, Сурьма.
источник
8675309
) и из нее создать тип, уникальный для этого числа (напримерZ8<Z6<Z7<Z5<Z3<Z0<Z9>>>>>>>
), который будет иметь элементы, отличные от любого другого типа. В C ++ все типы, которые могут быть сгенерированы из любого ввода, должны создаваться во время компиляции.Обычная критика - отсутствие овеществления. То есть объекты во время выполнения не содержат информации об их общих аргументах (хотя информация все еще присутствует в полях, методах, конструкторах и расширенном классе и интерфейсах). Это означает, что вы могли бы сыграть, скажем,
ArrayList<String>
вList<File>
. Компилятор выдаст вам предупреждения, но также предупредит вас, если вы возьметеArrayList<String>
его в качествеObject
ссылки и затем приведете к немуList<String>
. Плюсы в том, что с дженериками вы, вероятно, не должны выполнять кастинг, производительность лучше без лишних данных и, конечно, обратной совместимости.Некоторые люди жалуются, что вы не можете перегружать на основе общих аргументов (
void fn(Set<String>)
иvoid fn(Set<File>)
). Вместо этого вам нужно использовать лучшие имена методов. Обратите внимание, что эта перегрузка не требует повторной реализации, поскольку перегрузка является статической проблемой во время компиляции.Примитивные типы не работают с генериками.
Подстановочные знаки и границы довольно сложны. Они очень полезны. Если бы Java предпочитала интерфейсы неизменяемости и «говори-не-спрашивай», то более подходящими были бы обобщения на стороне объявления, а не на стороне использования.
источник
Object cs = new char[] { 'H', 'i' }; System.out.println(cs);
вы получите чепуху. Измените типcs
на,char[]
и вы получитеHi
.Обобщения Java отстой, потому что вы не можете сделать следующее:
Вам не нужно разделять каждый класс в приведенном выше коде, чтобы он работал так, как должен, если бы дженерики были на самом деле родовыми параметрами класса.
источник
Parser
мне нужно сделать заводской код еще более запутанным. Принимая во внимание, что если бы «универсальный» действительно означал «универсальный», тогда не было бы необходимости в фабриках и прочей чепухе, которая называется «шаблонами проектирования». Это одна из многих причин, по которым я предпочитаю работать на динамических языках.предупреждения компилятора об отсутствующих универсальных параметрах, которые не имеют смысла, делают язык бессмысленным, например:
public String getName(Class<?> klazz){ return klazz.getName();}
Дженерики плохо работают с массивами
Потерянная информация о типе делает отражение беспорядком отливки и клейкой ленты.
источник
HashMap
вместоHashMap<String, String>
.Я думаю, что другие ответы сказали это в некоторой степени, но не очень ясно. Одна из проблем с универсальными типами - потеря универсальных типов в процессе рефлексии. Так, например:
К сожалению, возвращаемые типы не могут сказать вам, что универсальные типы arr - String. Это тонкая разница, но это важно. Поскольку arr создается во время выполнения, универсальные типы стираются во время выполнения, поэтому вы не можете понять это. Как некоторые утверждают,
ArrayList<Integer>
выглядит так же, какArrayList<String>
с точки зрения отражения.Это может не иметь значения для пользователя Generics, но, скажем, мы хотели создать некоторую причудливую среду, которая использовала бы отражение, чтобы выяснить причудливые вещи о том, как пользователь объявил конкретные универсальные типы экземпляра.
Допустим, мы хотели, чтобы универсальная фабрика создала экземпляр,
MySpecialObject
потому что это конкретный универсальный тип, который мы объявили для этого экземпляра. Класс Factory не может опросить себя, чтобы узнать конкретный тип, объявленный для этого экземпляра, потому что Java стерла их.В обобщениях .Net вы можете сделать это, потому что во время выполнения объект знает, что это обобщенные типы, потому что компилятор скомпилировал его в двоичный файл. С стиранием Java не может этого сделать.
источник
Я мог бы сказать несколько хороших слов о дженериках, но это был не вопрос. Я мог бы пожаловаться, что они недоступны во время выполнения и не работают с массивами, но это было упомянуто.
Большое психологическое раздражение: я иногда попадаю в ситуации, когда генерики не могут работать. (Массивы - самый простой пример.) И я не могу понять, не могут ли дженерики справиться с работой или я просто тупой. Я ненавижу это. Что-то вроде дженериков должно работать всегда. Каждый раз, когда я не могу делать то, что хочу, используя Java-язык, я знаю, что проблема во мне, и я знаю, что если я продолжу настаивать, я доберусь до конца. С дженериками, если я стану слишком настойчивым, я могу тратить много времени.
Но настоящая проблема заключается в том, что генерики добавляют слишком много сложности для слишком маленькой выгоды. В самых простых случаях это может помешать мне добавить яблоко в список автомобилей. Хорошо. Но без обобщений эта ошибка вызвала бы исключение ClassCastException очень быстро во время выполнения с небольшим потерянным временем. Если я добавлю автомобиль с детским креслом, в котором есть ребенок, нужно ли мне предупреждение во время компиляции, что этот список предназначен только для автомобилей с детскими сиденьями, в которых есть шимпанзе? Список простых экземпляров Object начинает выглядеть как хорошая идея.
Общий код может содержать много слов и символов, занимать много дополнительного места и занимать намного больше времени для чтения. Я могу потратить много времени, чтобы заставить весь этот дополнительный код работать правильно. Когда это происходит, я чувствую себя безумно умным. Я также потратил впустую несколько часов или больше своего собственного времени, и я должен задаться вопросом, сможет ли кто-нибудь еще когда-нибудь разобраться в коде. Я хотел бы быть в состоянии передать его на техническое обслуживание людям, которые менее умны, чем я, или которые тратят меньше времени.
С другой стороны (я немного напрягся и чувствую необходимость обеспечить некоторый баланс), удобно использовать простые коллекции и карты для добавления, размещения и проверки при написании, и обычно это мало добавляет сложность кода ( если кто - то еще пишет коллекцию или карту) . И Java лучше в этом, чем C #. Кажется, ни одна из коллекций C #, которые я хочу использовать, никогда не работала с дженериками. (Признаюсь, у меня странные вкусы в коллекциях.)
источник
Fine. But without generics this error would throw a ClassCastException really quick at run time with little time wasted.
Это действительно зависит от того, сколько времени выполнения происходит между запуском программы и попаданием в строку кода. Если пользователь сообщает об ошибке и для ее воспроизведения требуется несколько минут (или, в худшем случае, часы или даже дни), проверка во время компиляции начинает выглядеть все лучше и лучше ...