Объясните разницу между шаблонами в C++ и дженериками в Java


Оглавление (нажмите, чтобы открыть):

Обобщение типа данных, generic

Начиная с Java 5 появились новые возможности для программирования, к которым следует отнести поддержку обобщенного программирования, названная в Java generic. Эта возможность позволяет создавать более статически типизированный код. Соответственно, программы становятся более надежными и проще в отладке.

generic являются аналогией с конструкцией «Шаблонов»(template) в С++. Ожидалось, что дженерики Java будут похожи на шаблоны C++. На деле оказалось, что различия между generic’ами Java и шаблонами С++ довольно велики. В основном generic в Java получился проще, чем их C++-аналог, однако он не является упрощенной версией шаблонов C++ и имеют ряд значительных отличий. Так, в языке появилось несколько новых концепций, касающихся generic’ов – это маски и ограничения.

Рассмотрим 2 примера без использования и с использованием generic. Пример без использования generic с приведением типа (java casting):

В данном примере программист знает тип данных, размещамый в List’e. Тем не менее, необходимо обратить особое внимание на приведение типа («java casting»). Компилятор может лишь гарантировать, что метод next() вернёт Object, но чтобы обеспечить присвоение переменной типа Integer правильным и безопасным, требуется java casting. Приведение типа не исключает возможности появления ошибки «Runtime Error» из-за невнимательности разработчика.

Возникает вопрос: «Как с этим бороться? Каким образом зарезервировать List для определенного типа данных?». Данную проблему решают дженерики generic. В следующем примере используется generic без приведения типов.

Обратите внимание на объявления типа для переменной integerList, которое указывает на то, что это не просто произвольный List, а List . Кроме этого теперь java casting выполняется автоматически.

В примере вместо приведения к Integer, был определен тип списка List. В этом заключается существенное отличие, и компилятор может проверить данный тип на корректность во время компиляции во всем коде. Эффект от generic особенно проявляется в крупных проектах: он улучшает читаемость и надежность кода в целом.

Свойства Generics

  • Строгая типизация.
  • Единая реализация.
  • Отсутствие информации о типе.

Объявление generic-класса

Объявить generic-класс совсем несложно. Пример такого объявления :

Пример использования generic-класса GenericSample :

Проблемы реализации generic

1. Wildcard

Рассмотрим процедуру dump, которой в качестве параметров передается Collection для вывода значений в консоль.

При передаче списка данных с целочисленным типом возникает ошибка. В этом примере список List нельзя передавать в качестве параметра в dump, так как он не является подтипом List .

Проблема состоит в том что данная реализация кода не эффективна, так как Collection не является полностью родительской коллекцией всех остальных коллекций, грубо говоря Collection имеет ограничения. Для решения этой проблемы используется Wildcard («?»), который не имеет ограничения в использовании, то есть имеет соответствие с любым типом, и в этом его плюсы. И теперь, мы можем вызвать это с любым типом коллекции.

2. Bounded Wildcard

Рассмотрим процедуру draw, которая рисует фигуры, наследующие свойства родителя Shape. Допустим у Shape есть наследник Circle, и его необходимо «изобразить».

Возникла ошибка, связанная с несовместимостью типов. В предложенном решении необходимо определить тип и его подтипы. Это есть так называемое «ограничение сверху». Для этого нужно вместо определить .

Использование позволяет использовать тип Cycle и всех его предков вполоть до Object.

3. Generic метод

Определим процедуру addAll, которая в качестве параметров получает массив данных Object[] и переносит его в коллекцию Collection

Ошибки, возникающие в последних строках связаны с тем, что нельзя просто вставить Object в коллекции неизвестного типа. Способ решения этой проблемы является использование «generic метода«. Для этого перед методом нужно объявить и использовать его.

Но все равно после выполнение останется ошибка в третьей строчке :

Допустим имеется функция, которая находит ближайший объект к точке Glyph из заданной коллекции. Glyph – это базовый тип, и может иметься неограниченное количество потомков этого типа. Также может иметься неограниченное количество коллекций, хранящих элементы, тип которых соответствует одному из этих потомков. Хотелось бы, чтобы функция могла работать со всеми подобными коллекциями, и возвращала элемент, тип которого совпадал бы с типом элемента коллекции, а не приводился к Glyph. Следующий пример не очень удачный:

Функция выглядит неплохо, но, тем не менее, не лишена недостатков. Получается так, что функции можно передать коллекцию любого типа. Это усложняет реализацию функции, порождая необходимость проверки типа элемента. Будет гораздо лучше написать так:

Теперь все встает на свои места, и в функцию можно передать только коллекцию, элементы которой реализуют интерфейс Glyph. generic сделал свое дело, код получился более типобезопасным.

4. Generic-классы

Наследование можно применять и для параметров generic-классов:

Как в методах, так и в классах можно задать более одного базового интерфейса, который должен реализовывать generic-параметр. Это делается при помощи следующего синтаксиса:

В данном примере generic-параметр должен реализовывать не только интерфейс Glyph, но и MoveableGlyph. Ограничений на количество интерфейсов, которые должен реализовывать переданный тип, нет. Но в класс можно передать только один, т.к. в Java нет множественного наследования. Типы в этом списке могут быть generic-типами, но ни один конкретный интерфейс не может появляться в списке более одного раза, даже с разными параметрами:

5. Bounded type argument

Метод копирования из одной коллекции в другую

Проблема в том, что две коллекции могут быть разных типов (несовместимость generic-типов). Для таких случаев был придуман Bounded type argument. Он нужен если метод, который мы разрабатываем, использовал бы определенный тип данных. Для этого нужно ввести (N принимает только значения M). Также можно корректно писать . (Принимает значения нескольких переменных).

6. Lower bounded wildcard

Метод нахождения максимума в коллекции

Ошибка возникает из-за того, что Test реализует интерфейс Comparable . Решение этой проблемы — Lower bounded wildcard («Ограничение снизу»). Суть заключается в том, что необходимо реализовывать метод не только для Т, но и для его супертипов (родительских типов). Например:

Теперь можно заполнить List , List или List .

6. Wildcard Capture

Реализация метода Swap в List

Ограничения generic

Невозможно создать массив generic’ов :

Невозможно создать массив generic-классов :

Преобразование типов

В Generics также можно манипулировать с информацией, хранящийся в переменных.

Наследование исключений в generic’ах

Возможность использовать параметр generic-класса или метода в throws позволяет при описании абстрактного метода не ограничивать разработчика, использующего класс или интерфейс, конкретным типом исключения. Но использовать тип, заданный в качестве параметра, в catch-выражениях нельзя.

Кроме того, можно сгенерировать исключение, тип которого задается generic-параметром, но экземпляр должен быть создан извне. Это ограничение порождается одним из ограничений Java generic’ов — нельзя создать объект, используя оператор new, тип которого является параметром generic’а.

Необходимо добавить, что тип, переданный в качестве параметра, должен обязательно быть наследником Throwable.

Таким образом, generic-и в Java получились проще и внесли несколько интересных концепций, таких как маски (wildcard) и ограничения, которые, добавили удобство при работе и помогли решить проблемы. Но, как и любое усложнение языка, эти нововведения затрудняют его понимание и изучение. Появление generic-ов сделало язык Java более выразительным и строгим; такие изменения только на пользу.

В чем основные отличия между шаблонами C ++ и дженериками Java?

Я начинаю программист на C ++, исходящий от Java.

Мне кажется, что C ++-шаблоны и Java-дженерики на самом деле очень похожи. Вы используете их в почти одинаковых случаях и одинаково. И они «чувствуют» то же самое. Однако я могу думать о двух различиях между системами:

В Java вы можете использовать типы классов только как аргументы типа, тогда как в C ++ любой тип подходит. Я не думаю, что это важно, потому что Java-аутобоксинг хорош.

Более существенная разница: шаблоны C ++ утиные. Это означает, что вы можете вызывать любую операцию на универсальном типе T . Если он не поддерживает операцию, возникает ошибка времени компиляции. Однако Java-дженерики не печатаются. По умолчанию все аргументы типа имеют тип Object , и вы можете указать что-то более конкретное по синтаксису T extends Something , что позволяет нам вызвать T , определенных в Something .

Второй момент весьма значителен. Однако, кроме того, каковы различия между этими двумя системами? Каковы плюсы и минусы каждого из них? Являются ли они похожими или разными в целом?

Дженерики (Java, обучающая статья)

категория
Java
дата 15.08.2014
автор Heorhi_Puhachou
голосов 169

Предисловие

За основу данной статьи была взята информация из 6-ой главы книги «Oracle Certified Professional Java SE 7 Programmers Exams 1Z0-804 and 1Z0-805». Она была немного изменена (кое-где обрезана, а кое-где дополнена с помощью Google и Википедии). Здесь показаны далеко не все нюансы дженериков — для более подробной информации следует обратиться к официальной документации. Приятного прочтения.

Введение

Обобщённое программирование — это такой подход к описанию данных и алгоритмов, который позволяет их использовать с различными типами данных без изменения их описания. В Java, начиная с версии J2SE 5.0, добавлены средства обобщённого программирования, синтаксически основанные на C++. Ниже будут рассматриваться generics (дженерики) или > — подмножество обобщённого программирования.

Допустим мы ничего не знаем о дженериках и нам необходимо реализовать специфический вывод на консоль информации об объектах различного типа (с использованием фигурных скобок).

Ниже пример реализации:

В вышеприведённом коде была допущена ошибка, из-за которой на консоли мы увидим следующее:

Теперь на время забудем об этом примере и попробуем реализовать тот же функционал с использованием дженериков (и повторим ту же ошибку):

Самое существенное отличие (для меня) в том, что при ошибке, аналогичной предыдущей, проблемный код не скомпилируется:

Думаю, многие согласятся, что ошибка компиляции «лучше» ошибки времени выполнения, т.к. чисто теоретически скомпилированный код с ошибкой может попасть туда, куда ему лучше бы и не попадать. Это очевидное достоинство дженериков. Теперь подробнее рассмотрим конструкции, относящиеся к дженерикам в этом примере. Для того, чтобы код скомпилировался, достаточно заменить строку

Посмотрим на декларацию BoxPrinter:

После имени класса в угловых скобках » » указано имя типа «Т», которое может использоваться внутри класса. Фактически Т – это тип, который должен быть определён позже (при создании объекта класса).


Внутри класса первое использование T в объявлении поля:

Здесь объявляется переменная дженерик-типа (generic type), т.о. её тип будет указан позже, при создании объекта класса BoxPrinter.

В main()-методе происходит следующее объявление:

Здесь указывается, что Т имеет тип Integer. Грубо говоря, для объекта value1 все поля Т-типа его класса BoxPrinter становятся полями типа Integer (private Integer val;).
Ещё одно место, где используется T:

Как и в декларации val с типом Т, вы говорите, что аргумент для конструктора BoxPrinter имеет тип T. Позже в main()-методе, когда будет вызван конструктор в new, указывается, что Т имеет тип Integer:

Теперь, внутри конструктора BoxPrinter, arg и val должны быть одного типа, так как оба имеют тип T. Например следующее изменение конструктора:

приведёт к ошибке компиляции.

Последнее место использования Т в классе – метод getValue():

Тут вроде тоже всё ясно – этот метод для соответствующего объекта будет возвращать значение того типа, который будет задан при его (объекта) создании.

При создании дженерик-классов мы не ограничены одним лишь типом (Т) – их может быть несколько:

Нет ограничений и на количество переменных с использующих такой тип:

Алмазный синтаксис (Diamond syntax)

Вернёмся немного назад к примеру со строкой кода:

Если типы не будут совпадать:

То мы получим ошибку при компиляции:

Немного лениво каждый раз заполнять типы и при этом можно ошибиться. Чтобы упростить жизнь программистам в Java 7 был введён алмазный синтаксис (diamond syntax), в котором можно опустить параметры типа. Т.е. можно предоставить компилятору определение типов при создании объекта. Вид упрощённого объявления:

Следует обратить внимание, что возможны ошибки связанные с отсутствием «<>» при использовании алмазного синтаксиса

В случае с примером кода выше мы просто получим предупреждение от компилятора, Поскольку Pair является дженерик-типом и были забыты «<>» или явное задание параметров, компилятор рассматривает его в качестве простого типа (raw type) с Pair принимающим два параметра типа объекта. Хотя такое поведение не вызывает никаких проблем в данном сегменте кода, это может привести к ошибке. Здесь необходимо пояснение понятия простого типа.

Посмотрим на вот этот фрагмент кода:

Теперь посмотрим на вот этот:

По результатам выполнения оба фрагмента аналогичны, но у них разная идея. В первом случае мы имеем место с простым типом, во вторым – с дженериком. Теперь сломаем это дело – заменим в обоих случаях

Для простого типа получим ошибку времени выполнения (java.lang.ClassCastException), а для второго – ошибку компиляции. В общем, это очень похоже на 2 самых первых примера. Если в двух словах, то при использовании простых типов, вы теряете преимущество безопасности типов, предоставляемое дженериками.

Универсальные методы (Generic methods)

По аналогии с универсальными классами (дженерик-классами), можно создавать универсальные методы (дженерик-методы), то есть методы, которые принимают общие типы параметров. Универсальные методы не надо путать с методами в дженерик-классе. Универсальные методы удобны, когда одна и та же функциональность должна применяться к различным типам. (Например, есть многочисленные общие методы в классе java.util.Collections.)

Рассмотрим реализацию такого метода:

Нам в первую очередь интересно это:

» » размещено после ключевых слов «public» и «static», а затем следуют тип возвращаемого значения, имя метода и его параметры. Такое объявление отлично от объявления универсальных классов, где универсальный параметр указывается после имени класса. Тело метода вполне обычное – в цикле все элементы списка устанавливаются в одно значение (val). Ну и в main()-методе происходит вызов нашего универсального метода:

Стоит обратить внимание на то, что здесь не задан явно тип параметра. Для IntList – это Integer и 100 тоже упаковывается в Integer. Компилятор ставит в соответствие типу Т – Integer.

Возможны ошибки, связанные с импортом List из java.awt вместо java.util. Важно помнить, что список из java.util является универсальным типом а список из java.awt — нет.

А сейчас вопрос – какая (-ие) из нижеприведённых строк откомпилируется без проблем?

Перед ответом на этот вопрос следует учесть, что List – интерфейс, ArrayList наследуется от List; Number — абстрактный класс и Integer наследуется от Number.

Ответ с пояснением:
Первый вариант неправильный, т.к. нельзя создавать объект интерфейса.
Во втором случае мы создаем объект типа ArrayList и ссылку на него базового для ArrayList класса. И там, и там дженерик-тип одинаковый – всё правильно.
В третьем и четвёртом случае будет иметь ошибка компиляции, т.к. дженерик-типы должны быть одинаковыми (связи наследования здесь никак не учитываются).

Условие одинаковости дженерик-типов может показаться не совсем логичным. В частности хотелось бы использовать конструкцию под номером 3. Почему же это не допускается?

Будем думать от обратного – допустим 3-ий вариант возможен. Рассмотрим такой код:

Первая строка кода смотрится вполне логично, т.к. ArrayList наследуется от List , а Integer наследуется от Number. Однако допуская такую возможность мы получили бы ошибку в третьей строке этого кода, ведь динамический тип IntList — ArrayList Integer>, т.е. происходит нарушение типобезапасности (присвоение значение Float там, где ожидается Integer) и в итоге была бы получена ошибка компилятора. Дженерики созданы, чтобы избегать ошибок такого рода, поэтому существует данное ограничение. Но тем не менее это неудобное ограничение и Java поддерживает маски для его обхода.

Wildcards (Маски)

Сейчас будут рассмотрены Wildcard Parameters (wildcards). Этот термин в разных источниках переводится по-разному: метасимвольные аргументы, подстановочные символы, групповые символы, шаблоны, маски и т.д. В данной статье я буду использовать «маску», просто потому, что в ней меньше букв…

Как было написано выше вот такая строка кода не скомпилируется:

Но есть возможность похожей реализации:

Под маской мы будем понимать вот эту штуку – » «.

А сейчас пример кода использующего маску и пригодного к компиляции:

Метод printList принимает список, для которого в сигнатуре использована маска:

И этот метод работает для списков с различными типами данных (в примере Integer и String).

Однако вот это не скомпилируется:

Почему не компилируется? При использовании маски мы сообщаем компилятору, чтобы он игнорировал информацию о типе, т.е. — неизвестный тип. При каждой попытке передачи аргументов дженерик-типа компилятор Java пытается определить тип переданного аргумента. Однако теперь мы используем метод add () для вставки элемента в список. При использовании маски мы не знаем, какого типа аргумент может быть передан. Тут вновь видна возможность ошибки, т.к. если бы добавление было возможно, то мы могли бы попытаться вставить в наш список, предназначенный для чисел, строковое значение. Во избежание этой проблемы, компилятор не позволяет вызывать методы, которые могут добавить невалидный тип — например, добавить значение типа Float, с которым мы потом попробуем работать как с Integer (или String — по маске не определишь точно). Тем не менее есть возможность получить доступ к информации, хранящейся в объекте, с использованием маски, как это было показано выше.

И ещё один маленький пример:

Тут не возникнет проблем компиляции. Однако нехорошо, что переменная numList хранит список со строками. Допустим нам нужно так объявить эту переменную, чтобы она хранила только списки чисел. Решение есть:

Данный код не скомпилируется, а всё из-за того, что с помощью маски мы задали ограничение. Переменная numList может хранить ссылку только на список, содержащий элементы унаследованные от Number, а всё из-за объявления: List numList. Тут мы видим, как маске задаётся ограничение – теперь numList предназначен для списка с ограниченным количеством типов. Double как и Integer наследуется от Number, поэтому код приведённый ниже скомпилируется.

То, что было описано выше называется ограниченными масками (Bounded wildcards). Применение таких конструкций может быть весьма красивым и полезным. Допустим нам необходимо посчитать сумму чисел различного типа, которые хранятся в одном списке:

Double-тип был использован для переменной result т.к. он без проблем взаимодействует с другими числовыми типами (т.е. не будет проблем с приведением типов).

В завершение этой темы добавлю, что аналогично ключевому слову extends в подобного рода выражениях может использоваться ключевое слово super — » «. Выражение означает, что вы можете использовать любой базовый тип (класс или интерфейс) типа Х, а также и сам тип Х. Пара строк, которые нормально скомпилируются:

На этом все. Надеюсь, данная статья была полезной.

Если Вам понравилась статья, проголосуйте за нее

Каковы различия между Generics в C#и Java. и шаблонами в C++?

generic java (11)

Сам Андерс Хейлсберг рассказал о различиях здесь « Дженерики в C #, Java и C ++ ».

В основном я использую Java, а дженерики — относительно новые. Я продолжаю читать, что Java приняла неправильное решение или что .NET имеет лучшие реализации и т. Д. И т. Д.

Итак, каковы основные различия между C ++, C #, Java в дженериках? Плюсы / минусы каждого?

Я добавлю свой голос к шуму и сделаю удар, чтобы прояснить ситуацию:

Мастер Йода рекомендует:  Твиттер на Vue.js руководство для начинающих

C # Generics позволяет объявить что-то вроде этого.

и тогда компилятор помешает вам помещать в список вещи, которые не являются Person .
За кулисами компилятор C # просто кладет List

в DLL-файл .NET, но во время выполнения компилятор JIT идет и создает новый набор кода, как если бы вы написали специальный класс списка только для того, чтобы содержать людей — что-то как ListOfPerson .

Преимущество этого в том, что он делает это очень быстро. Там нет кастинга или каких-либо других вещей, и поскольку в dll содержится информация о том, что это список людей, другой код, который смотрит на него позже, используя отражение, может сказать, что он содержит объекты Person (поэтому вы получаете intellisense и т. Д.).

Недостатком этого является то, что старый код C # 1.0 и 1.1 (до того, как они добавили генерики) не понимает этот новый List , поэтому вам нужно вручную преобразовать данные обратно в обычный старый List чтобы взаимодействовать с ними. Это не проблема, потому что двоичный код C # 2.0 не совместим с предыдущим. Единственный раз, когда это произойдет, — если вы обновляете старый код C # 1.0 / 1.1 до C # 2.0

Java Generics позволяет вам объявить что-то вроде этого.

На поверхности он выглядит одинаково, и это своего рода. Компилятор также не позволит вам помещать в список вещи, которые не являются Person .

Разница в том, что происходит за кулисами. В отличие от C #, Java не идет и не создает специальный ListOfPerson — он просто использует простой старый ArrayList который всегда был на Java. Когда вы получаете вещи из массива, обычный Person p = (Person)foo.get(1); кастинг-танец еще предстоит сделать. Компилятор сохранит вам нажатия клавиш, но скорость попадания / кастинга по-прежнему поступила так же, как и всегда.
Когда люди упоминают «Type Erasure», это то, о чем они говорят. Компилятор вставляет вам броски, а затем «стирает» тот факт, что он должен быть списком Person не только Object

Преимущество такого подхода заключается в том, что старый код, который не понимает дженериков, не нуждается в заботе. Он по-прежнему имеет дело с тем же старым ArrayList что и всегда. Это более важно в java-мире, потому что они хотели поддерживать компиляцию кода с использованием Java 5 с помощью дженериков, а его запуск на старых 1,4 или предыдущих JVM, которые Microsoft намеренно решила не беспокоиться.

Недостатком является упоминание о скорости, о которой я упомянул ранее, а также о том, что псевдокласс класса ListOfPerson отсутствует или что-либо подобное, ListOfPerson в файлы .class, код, который смотрит на него позже (с отражением или если вы вытаскиваете его из другого коллекция, где она была преобразована в Object или так далее) никак не может сказать, что она предназначена для списка, содержащего только Person а не только любой другой список массивов.

Шаблоны C ++ позволяют объявлять что-то вроде этого

Это похоже на C # и Java generics, и он будет делать то, что, по вашему мнению, должен делать, но за кулисами происходят разные вещи.

Он имеет больше всего общего с C # generics тем, что он создает специальные pseudo-classes а не просто выбрасывает информацию о типе, как java, но это совершенно другой чайник.

Оба C # и Java производят вывод, который предназначен для виртуальных машин. Если вы напишете код, в котором есть класс Person , в обоих случаях некоторая информация о классе Person войдет в файл .dll или .class, и JVM / CLR сделает это с этим.

C ++ создает необработанный двоичный код x86. Все не является объектом, и нет никакой базовой виртуальной машины, которая должна знать о классе Person . Нет бокса или unboxing, и функции не должны принадлежать к классам или вообще что-либо.

Из-за этого компилятор C ++ не накладывает ограничений на то, что вы можете делать с шаблонами — в основном любой код, который вы могли бы написать вручную, вы можете получить шаблоны для записи для вас.
Наиболее очевидным примером является добавление вещей:

В C # и Java система generics должна знать, какие методы доступны для класса, и нужно передать это на виртуальную машину. Единственный способ сказать это — либо жестко кодировать фактический класс, либо использовать интерфейсы. Например:


Этот код не будет компилироваться на C # или Java, потому что он не знает, что тип T фактически предоставляет метод под названием Name (). Вы должны сказать это — в C #, как это:

И тогда вы должны убедиться, что вещи, которые вы передаете addNames, реализуют интерфейс IHasName и т. Д. Синтаксис java отличается ( ), но он испытывает те же проблемы.

«Классическим» случаем для этой проблемы является попытка написать функцию, которая делает это

Вы не можете написать этот код, потому что нет способов объявить интерфейс с помощью метода + . Ты облажался.

C ++ не страдает ни от одной из этих проблем. Компилятор не заботится о передаче типов к любой VM — если оба объекта имеют функцию .Name (), они будут скомпилированы. Если они этого не сделают, это не произойдет. Просто.

Итак, у вас есть это 🙂

Шаблоны C ++ на самом деле намного мощнее, чем их C # и Java-аналоги, поскольку они оцениваются во время компиляции и поддерживают специализацию. Это позволяет метапрограммирование шаблонов и делает компилятор C ++ эквивалентным машине Тьюринга (т.е. во время процесса компиляции вы можете вычислить все, что можно вычислить с помощью машины Тьюринга).

11 месяцев поздно, но я думаю, что этот вопрос готов к некоторым материалам Java Wildcard.

Это синтаксическая особенность Java. Предположим, у вас есть метод:

И предположим, что вам не нужно ссылаться на тип T в теле метода. Вы объявляете имя T, а затем используете его только один раз, так почему вы должны думать о имени для него? Вместо этого вы можете написать:

Вопросительный знак просит компилятор сделать вид, что вы объявили нормальный именованный тип, который должен появиться только один раз в этом месте.

Вы ничего не можете сделать с помощью подстановочных знаков, которые вы также не можете использовать с параметром named type (как это всегда делается на C ++ и C #).

Уже есть много хороших ответов на то, каковы различия, поэтому позвольте мне дать немного другую точку зрения и добавить, почему .

Как уже объяснялось, основным отличием является стирание типа , то есть тот факт, что компилятор Java стирает общие типы, и они не попадают в сгенерированный байт-код. Однако возникает вопрос: зачем кому-то это делать? Это не имеет смысла! Или это?

Ну, а какая альтернатива? Если вы не реализуете дженерики на языке, где вы их реализуете? И ответ: в виртуальной машине. Что нарушает совместимость.

С другой стороны, стирание стилей позволяет смешивать общие клиенты с не-генерическими библиотеками. Другими словами: код, который был скомпилирован на Java 5, все еще может быть развернут в Java 1.4.

Однако Microsoft решила отказаться от совместимости для дженериков. Вот почему .NET Generics «лучше», чем Java Generics.

Конечно, Солнце не идиоты или трусы. Причина, по которой они «выкидывались», заключалась в том, что Java была значительно старше и более распространена, чем .NET, когда они вводили дженерики. (Они были представлены примерно в то же время в обоих мирах.) Нарушение обратной совместимости было бы огромной болью.

Еще один способ: в Java, Generics являются частью языка (что означает, что они применяются только к Java, а не к другим языкам), в .NET они являются частью виртуальной машины (что означает, что они применяются ко всем языкам, а не просто C # и Visual Basic.NET).

Сравните это с функциями .NET, такими как LINQ, лямбда-выражения, локальные переменные типа, анонимные типы и деревья выражений: все это языковые функции. Вот почему существуют тонкие различия между VB.NET и C #: если эти функции были частью VM, они были бы одинаковыми на всех языках. Но CLR не изменился: в .NET 3.5 SP1 он все тот же, как и в .NET 2.0. Вы можете скомпилировать программу на C #, которая использует LINQ с компилятором .NET 3.5 и все еще запускать ее на .NET 2.0 при условии, что вы не используете библиотеки .NET 3.5. Это не сработает с generics и .NET 1.1, но оно будет работать с Java и Java 1.4.

Самая большая жалоба — стирание типа. При этом дженерики не применяются во время выполнения. Вот ссылка на некоторые документы Sun по этому вопросу .

Генерики реализуются с помощью стирания типа: информация об общем типе присутствует только во время компиляции, после чего она удаляется компилятором.

В Википедии есть отличные рецензии, сравнивающие как генераторы Java / C #, так и шаблоны Java generics / C ++ . Основная статья о Generics кажется немного загроможденной, но в ней есть хорошая информация.

Похоже, что среди других очень интересных предложений есть один о совершенствовании дженериков и отмене обратной совместимости:

В настоящее время генерические средства реализуются с использованием стирания, что означает, что общая информация типа недоступна во время выполнения, что затрудняет запись какого-либо кода. Эти средства были реализованы таким образом, чтобы поддерживать обратную совместимость со старым нестандартным кодом. Обоснованные генерические файлы будут предоставлять информацию об общем типе во время выполнения, что приведет к потере устаревшего не общего кода. Тем не менее, Нил Гафтер предложил сделать типы повторно доступными только в том случае, если они указаны, чтобы не нарушить обратную совместимость.

C ++ редко использует терминологию «generics». Вместо этого используется слово «шаблоны» и более точно. Шаблоны описывают один метод для достижения общего дизайна.

Шаблоны C ++ сильно отличаются от того, что реализуют как C #, так и Java по двум основным причинам. Первая причина заключается в том, что шаблоны C ++ не только допускают аргументы типа компиляции, но и аргументы const-value для компиляции: шаблоны могут быть заданы как целые числа, так и даже сигнатуры функций. Это означает, что во время компиляции вы можете делать довольно забавные вещи, например, вычисления:

Этот код также использует другую отличительную особенность шаблонов C ++, а именно специализированную специализацию. Код определяет один шаблон класса, product с одним аргументом значения. Он также определяет специализацию для этого шаблона, которая используется всякий раз, когда аргумент оценивается в 1. Это позволяет мне определить рекурсию над определениями шаблонов. Я считаю, что это было впервые обнаружено Андреем Александреску .

Специализация шаблона важна для C ++, поскольку она допускает структурные различия в структурах данных. Шаблоны в целом являются средством объединения интерфейса между типами. Однако, хотя это желательно, все типы не могут рассматриваться одинаково внутри реализации. Шаблоны C ++ учитывают это. Это очень та же разница, что и ООП между интерфейсом и реализацией с переопределением виртуальных методов.

Шаблоны C ++ необходимы для его алгоритмической парадигмы программирования. Например, почти все алгоритмы для контейнеров определяются как функции, которые принимают тип контейнера в качестве типа шаблона и обрабатывают их равномерно. Собственно, это не совсем правильно: C ++ не работает с контейнерами, а скорее на диапазонах , которые определены двумя итераторами, указывая на начало и конец конца контейнера. Таким образом, все содержимое ограничено итераторами: begin

В Java дженерики — это только уровень компилятора, поэтому вы получаете:

Обратите внимание, что тип ‘a’ — это список массивов, а не список строк. Таким образом, тип списка бананов будет равен () списку обезьян.

И Java, и C # представили дженерики после их первого выпуска языка. Тем не менее, существуют различия в том, как изменились основные библиотеки при введении дженериков. Генераторы C # — это не просто магия компилятора, и поэтому невозможно было создать существующие классы библиотеки, не нарушая совместимость с предыдущими версиями .

Например, в Java существующая структура коллекций была полностью обобщена . В Java нет как универсальной, так и старой версии классов коллекций. В некотором смысле это намного чище — если вам нужно использовать коллекцию на C #, есть очень мало оснований идти с не-универсальной версией, но эти унаследованные классы остаются на месте, загромождая пейзаж.

Другим заметным отличием являются классы Enum в Java и C #. Java Enum имеет это несколько извилистое определение:

(см. очень ясное объяснение Анжелики Лангер, почему именно так. По сути, это означает, что Java может предоставить безопасный доступ типа от строки до значения Enum:

Сравните это с версией C #:

Поскольку Enum уже существовал на C #, прежде чем дженерики были введены в язык, определение не могло измениться без нарушения существующего кода. Таким образом, подобно коллекциям, он остается в основных библиотеках в этом устаревшем состоянии.

Каковы различия между «универсальным» типы в C++ и Java?

У Java есть дженерики, а C++ обеспечивает очень сильную модель программирования с template s. Итак, в чем же разница между обобщениями C++ и Java?

12 ответов

Возможно, для Вашего проекта будут необходимы бесплатные векторные карты. На нашем сайте представлены карты для всех стран.

Между ними есть большая разница. В C++ вам не нужно указывать класс или интерфейс для универсального типа. Вот почему вы можете создавать действительно универсальные функции и классы с оговоркой более свободной типизации.

Приведенный выше метод добавляет два объекта одного типа и может использоваться для любого типа T, для которого доступен оператор «+».

В Java вы должны указать тип, если вы хотите вызывать методы переданных объектов, например:

В C++ универсальные функции/классы могут быть определены только в заголовках, так как компилятор генерирует разные функции для разных типов (с которыми он вызывается). Таким образом, сборка идет медленнее. В Java компиляция не имеет серьезных проблем, но Java использует технику, называемую «стирание», при которой универсальный тип стирается во время выполнения, поэтому во время выполнения Java фактически вызывает.

Таким образом, общее программирование на Java не очень полезно, это всего лишь небольшой синтаксический сахар, чтобы помочь с новой конструкцией foreach.

РЕДАКТИРОВАТЬ: мнение выше о полезности было написано моложе себя. Обобщения Java помогают, конечно, с безопасностью типов.

Java Generics сильно отличается от шаблонов C++.

В основном в C++ шаблоны представляют собой прославленный набор препроцессоров/макросов (Примечание: поскольку некоторые люди не могут понять аналогию, я не говорю, что обработка шаблонов — это макрос). В Java они в основном являются синтаксическим сахаром для минимизации шаблонного преобразования объектов. Вот довольно приличный введение в шаблоны C++ против обобщений Java.

Чтобы уточнить этот момент: когда вы используете шаблон C++, вы в основном создаете еще одну копию кода, как если бы вы использовали #define макро. Это позволяет вам делать такие вещи, как иметь int параметры в определениях шаблонов, которые определяют размеры массивов и тому подобное.

Java не работает так. В Java все объекты выходят из java.lang.Object итак, пре-Generics, вы бы написали такой код:

потому что все типы коллекций Java использовали Object в качестве базового типа, чтобы вы могли помещать в них что угодно. Java 5 катится и добавляет дженерики, чтобы вы могли делать такие вещи, как:

И это все, что является универсальным Java: обертки для приведения объектов. Это потому, что Java Generics не улучшены. Они используют стирание типа. Это решение было принято, потому что Java Generics появился настолько поздно, что они не хотели нарушать обратную совместимость ( Map можно использовать всякий раз, когда Map называется). Сравните это с .Net/C#, где стирание типов не используется, что приводит к разного рода различиям (например, вы можете использовать примитивные типы и IEnumerable а также IEnumerable не имеют никакого отношения друг к другу).

И класс, использующий дженерики, скомпилированные с помощью компилятора Java 5+, можно использовать в JDK 1.4 (при условии, что он не использует никаких других функций или классов, требующих Java 5+).

Вот почему Java Generics называются синтаксический сахар.

Но это решение о том, как сделать дженерики, имеет глубокие последствия настолько, что (превосходно) Java Generics FAQ возникла, чтобы ответить на многие, многие вопросы о Java Generics.

Шаблоны C++ имеют ряд функций, которых нет в Java Generics:

Использование аргументов примитивного типа.

Java не позволяет использовать аргументы примитивного типа в обобщениях.

Использование аргументы типа по умолчанию, это одна из функций, которую мне не хватает в Java, но для этого есть причины обратной совместимости;

  • Java позволяет ограничивать аргументы.
  • Действительно нужно подчеркнуть, что вызовы шаблонов с разными аргументами действительно являются разными типами. Они даже не разделяют статических членов. В Java это не так.

    Помимо различий с дженериками, для полноты базовое сравнение C++ и Java (а также другой).

    И я могу также предложить Мышление на Java. Как программист C++, многие понятия, такие как объекты, уже будут второй натурой, но есть небольшие различия, поэтому может быть целесообразно иметь вводный текст, даже если вы просматриваете части.

    Многое из того, что вы изучите при изучении Java, — это все библиотеки (обе стандартные — то, что входит в JDK — и нестандартные, которые включают в себя такие часто используемые вещи, как Spring). Синтаксис Java более многословен, чем синтаксис C++, и не имеет большого количества функций C++ (например, перегрузка операторов, множественное наследование, механизм деструктора и т.д.), Но это не делает его строго подмножеством C++.

    C++ имеет шаблоны. В Java есть дженерики, которые выглядят как шаблоны C++, но они очень разные.

    Шаблоны работают, как следует из названия, предоставляя компилятору шаблон (подождите его. ), который он может использовать для генерации безопасного кода, заполняя параметры шаблона.

    Обобщения, как я их понимаю, работают наоборот: параметры типа используются компилятором для проверки того, что код, использующий их, безопасен для типов, но полученный код вообще генерируется без типов.

    Думайте о шаблонах C++ как о действительно хорошей макросистеме, а обобщения Java как о инструменте для автоматического создания типов типов.

    Еще одна особенность, которая есть в шаблонах C++, которых нет в обобщениях Java, — это специализация. Это позволяет вам иметь другую реализацию для определенных типов. Таким образом, вы можете, например, иметь высоко оптимизированную версию для int, но при этом иметь общую версию для остальных типов. Или вы можете иметь разные версии для типов указателей и не указателей. Это очень удобно, если вы хотите работать с разыменованным объектом при передаче указателя.

    Есть отличное объяснение этой темы в Java Generics и КоллекцииМорис Нафталин, Филипп Вадлер. Я очень рекомендую эту книгу. Цитировать:


    Обобщения в Java напоминают шаблоны в C++. Синтаксис намеренно похожи и семантика намеренно другой. Семантически, дженерики Java определяется стиранием, где как C++ шаблоны определяются расширением.

    Пожалуйста, прочитайте полное объяснение вот.

    По сути, шаблоны AFAIK, C++ создают копию кода для каждого типа, в то время как шаблоны Java используют точно такой же код.

    Да, вы можете сказать, что шаблон C++ эквивалентен общему понятию Java (хотя правильнее было бы сказать, что дженерики Java по своей сути эквивалентны C++)

    Если вы знакомы с механизмом шаблонов C++, вы можете подумать, что дженерики похожи, но сходство поверхностно. Обобщения не генерируют новый класс для каждой специализации, и при этом они не разрешают «шаблонное метапрограммирование».

    Обобщения Java (и C#) кажутся простым механизмом замены типов во время выполнения.
    Шаблоны C++ представляют собой конструкцию времени компиляции, которая дает вам возможность изменить язык в соответствии с вашими потребностями. На самом деле это чисто функциональный язык, который компилятор выполняет во время компиляции.

    Еще одно преимущество шаблонов C++ — это специализация.

    Теперь, если вы вызываете sum с помощью указателей, будет вызван второй метод, если вы вызовете sum с не указательными объектами, будет вызван первый метод, и если вы вызовете sum с Special объекты, третий будет называться. Я не думаю, что это возможно с Java.

    Я подведу итог в одном предложении: шаблоны создают новые типы, обобщенные элементы ограничивают существующие типы.

    Ответ ниже — из книги «Взломать решения для интервьюирования в коде» главы 13, которая, на мой взгляд, очень хорошая.

    Реализация дженериков Java коренится в идее «стирания типов». Этот метод исключает параметризованные типы, когда исходный код транслируется в байт-код виртуальной машины Java (JVM). Например, предположим, что у вас есть код Java ниже:

    Во время компиляции этот код переписывается в:

    Мастер Йода рекомендует:  Создание веб-приложения с двухфакторной аутентификацией на Angular 7

    Использование дженериков Java не сильно изменило наши возможности; это только сделало вещи немного красивее. По этой причине дженерики Java иногда называют «синтаксическим сахаром:».

    Это сильно отличается от C++. В C++ шаблоны по сути являются прославленным набором макросов, при этом компилятор создает новую копию кода шаблона для каждого типа. Доказательством этого является тот факт, что экземпляр MyClass не будет использовать статическую переменную совместно с MyClass. Однако экземпляры типа MyClass будут использовать статическую переменную.

    В Java статические переменные являются общими для всех экземпляров MyClass независимо от параметров различных типов.

    Шаблоны Java и шаблоны C++ имеют ряд других отличий. Они включают:

    • Шаблоны C++ могут использовать примитивные типы, такие как int. Ява не может и должна вместо этого используйте Integer.
    • В Java вы можете ограничить параметры типа шаблона определенный тип. Например, вы можете использовать дженерики для реализации CardDeck и укажите, что параметр типа должен расширяться от Карточная игра.
    • В C++ параметр типа может быть создан, тогда как в Java нет поддержать это.
    • В Java параметр типа (то есть Foo в My > поделиться : facebook, twitter Jaycee, 01 Jul 2020 в 15:59

    Этот код на самом деле не так и кроме мелких глюков ( template пропущен, синтаксис специализации выглядит иначе), частичная специализация не работает на шаблонах функций, только на шаблонах классов. Код, однако, будет работать без частичной специализации шаблона, вместо этого используется простая старая перегрузка:

    Шаблоны — это не что иное, как макросистема. Синтаксис сахар. Они полностью раскрываются перед фактической компиляцией (или, по крайней мере, компиляторы ведут себя так, как если бы это было так).

    Допустим, мы хотим две функции. Одна функция берет две последовательности (список, массивы, векторы, что угодно) чисел и возвращает их внутреннее произведение. Другая функция принимает длину, генерирует две последовательности этой длины, передает их первой функции и возвращает ее результат. Уловка в том, что мы можем ошибиться во второй функции, так что эти две функции на самом деле не имеют одинаковую длину. Нам нужен компилятор, чтобы предупредить нас в этом случае. Не когда программа запущена, а когда она компилируется.

    В Java вы можете сделать что-то вроде этого:

    В C# вы можете написать почти то же самое. Попробуйте переписать его на C++, и он не скомпилируется, жалуясь на бесконечное расширение шаблонов.

    Каковы различия между шаблонами C ++ и Java / C # дженериков и каковы пределы? [закрыто]

    December 2020

    4.9k раз

    Я прочитал интересную статью / Тема / Обсуждение с здесь и я получил следующие вопросы:

    • Каковы ограничения дженериков Java / C #?
    • Что можно с шаблонами C ++, что невозможно с Java дженериков / C #?

    Редактирование- Более рекомендуемые вопросов по Эрику Липперт

    • Каковы некоторые модели, которые возможны с C # дженериков, но невозможно с помощью шаблонов C ++?
    • В чем разница между истинными общими типами и тип стирания родовых типов Java в C # ‘s?

    4 ответы

    Мотивацией для Java Дженерики всегда была обеспечить безопасность типов, сохраняя при этом обратную совместимость. ВС реализуется дженерики путем добавления проверки типов, а затем удаления общего типа в качестве части компиляции. Код, как:

    Никакое знание типа списка не делает его в класс во время выполнения, но некоторые instanceof с могут быть отброшена компилятором как безопасные.

    Так как компилятор не записывает общие типы в скомпилированные файлы классов, и поэтому приведенные выше list.get >, не может быть никаких шаблонов специализаций , как в C ++. List не могут быть упакованы в последовательность битов. Все общие типы типов, в отличие от шаблонов в C ++ как:

    которые могут быть использованы для анализа размерностей, и держать людей от добавления длины к областям.

    Во- первых, вы можете прочитать мою статью 2009 по этой теме .

    Основное отличие , на мой взгляд между шаблонами C ++ и C # дженериков, что шаблоны C ++ фактически полностью перекомпилировать код при построении шаблона . Плюсы и минусы С ++ подхода много:

    PRO: Вы можете эффективно создавать ограничения, как «аргумент типа T должен иметь оператор сложения»; если код содержит пару Ts добавила друг к другу, то шаблон не будет скомпилирован, если вы строите его с аргументом типа, который не допускает добавление.

    CON: Вы можете случайно создать недокументированные ограничения, такие как «аргумент типа T должен иметь оператор сложения».

    В C # вы должны сказать , что эти ограничения , которое помогает пользователю, но вы ограничены лишь небольшой набор возможных ограничений: интерфейсы, базовые классы, значения против ограничений ссылочного типа и конструктор по умолчанию, и это все.

    PRO: семантический анализ может быть совершенно разным для двух различных конструкций. Если вы хотите, что это является удивительным.

    CON: семантический анализ может быть совершенно разным для двух различных конструкций. Если вы не хотите, что это ошибка ждет, чтобы случиться.

    В C # семантический анализ не делается один раз , независимо от того , сколько раз строится тип, и поэтому требуется для работы с любым типом аргумента , который соответствует ограничениям, а не только аргументы типа, которые фактически поставляются.

    PRO: Вы только генерировать код для одних и тех конструкций, которые необходимо.

    CON: Вы генерируете код для всех конструкций, которые вы используете.

    Шаблоны могут привести к Codegen , чтобы получить большой. В C #, интерлейкин для универсального типа генерируется один раз, а затем во время выполнения джиттера делает Codegen для всех типов программа использует. Это небольшое снижение производительности, но несколько смягчено тот факт , что джиттер на самом деле только генерирует код один раз для всех аргументов ссылочного типа . Так что если у вас есть List и List то JITted код генерируется только один раз и используется для обоих. List и List контрастный jits код дважды.

    PRO: когда вы используете библиотеку шаблонов, у вас есть исходный код прямо там.

    CON: использовать библиотеку шаблонов, вы должны иметь исходный код.

    В C #, родовые типы являются типами первого класса. Если вы будете придерживаться их в библиотеке, вы можете использовать эту библиотеку в любом месте без необходимости грузить исходный код.

    PRO: Шаблоны позволяют шаблон метапрограммирования.

    CON: Шаблон метапрограммированием трудно понять, для новичков.

    CON: Система шаблонов на самом деле не допускает некоторые топологии типа, которые очень просты в общей системе.

    Например, я полагаю, что это будет трудно сделать что-то подобное в C ++:

    В дженериков C #, нет проблем. Во время выполнения типа построен только один раз для всех аргументов ссылочного типа.

    Но в шаблонах C ++, что происходит , когда у вас есть D ? Тип интерьера создает поле типа D .S> , так что нам нужно построить этот тип. Но этот тип создает поле типа D .S>.S> . и так далее до бесконечности.

    Каковы ограничения дженериков Java / C #?

    Java дженерики ограничены, потому что это не представляется возможным сделать некоторые трюки, как в C ++.

    Для доказательства претензии здесь пример C ++ , который невозможно воспроизвести только шаблоны в Java.

    Политика программирование на основе является способом ограничить использование (шаблонный) класса других (возможно) шаблонных классы по наследованию во время компиляции.

    Что такое большое дело о компиляции дженерик времени против времени выполнения дженерик?

    Дело в том, что компилятор знает все о возможном поведении во время выполнения классовых / шаблонов, поэтому он может делать тяжелые оптимизации, которые (в настоящее время) невозможно с C # / Java / независимо от среды выполнения / компилятора.

    ДРУГАЯ хорошая сторона этого является то, что компилятор может гарантировать, что instatiation комбинации шаблонов является действительным, что означает, что нет никакой возможности, что во время выполнения-ошибки могут происходить как в Java / C #, когда программист хочет, чтобы instatiate новых объектов с недопустимое сочетание.

    Что является недостатком дженериков C ++?

    Недостатком является то, что шаблоны могут получить действительно сложно читать и понимать и отлаживать. Это (возможно) одна из причин, почему разработчики Java не хотят иметь такой зверь на языке.

    Что можно с Дженерики C ++, что невозможно с Java / C # дженериков?

    В C ++ это можно использовать другие шаблоны в качестве параметров шаблона, который невозможно в C # / Java и которая позволяет элегантные трюки, как шаблон-метапрограммирования.

    Согласно MSDN основные различия между C # генериков и шаблонов C ++ являются:

    • C # генериков не обеспечивают такую ​​же степень гибкости в качестве шаблонов C ++. Например, это не возможно вызвать арифметические операторы в C #, универсальный класс, хотя можно назвать определенные пользователем операторы.
    • C # не позволяет параметры шаблона не-типа, такие как шаблон C <>.
    • C # не поддерживают явную специализацию; то есть Заказная реализация шаблона для определенного типа.
    • C # не поддерживает частичную специализацию: пользовательскую реализацию для подмножества аргументов типа.
    • C # не позволяют параметр типа для использования в качестве базового класса для универсального типа.
    • C # не позволяет изменять параметры типа, чтобы иметь типы по умолчанию.
    • В C #, общий параметр типа не может сам по себе быть общим, хотя построенные типы могут быть использованы в качестве воспроизведенных. C ++ действительно позволяет параметры шаблона.

    Однако бывают случаи, когда вы можете работать вокруг некоторых из этих проблем с помощью методов расширения.

    Возможно дженериков C ++, ни с C # дженериков ни Java дженериков: истинный шаблон метапрограммированием (Тьюринга во время компиляции).

    редактировать

    Стандарт C ++ не было ограничений на параметры типа в то время как C # и Java есть. Подталкивания имеет что — то подобное ( Повысьте Concept Check Library ). Но так как C ++ 11 теперь вы можете использовать , чтобы получить что — то подобное.

    What are the main differences between C++ templates and Java generics?

    I’m a beginning C++ programmer coming from Java.


    It feels to me that C++ templates and Java generics are actually pretty similar. You use them in pretty much the same occasions and in the same way. And they ‘feel’ the same. However I can think of two differences between the systems:

    In Java you can only use class-types as type arguments, while in C++ any type fits. I don’t think this is significant because Java autoboxing is good.

    A more significant difference: C++ templates are duck typed. Meaning you can call any operation on a generic type T . If it doesn’t support the operation, a compile time error is raised. Java generics however aren’t duck typed. By default all type arguments are of type Object , and you can specify something more specific by the syntax T extends Something , which allows us to call on T methods defined in Something .

    The second point is rather significant. However apart from that, what are the differences between the two systems? What are the pros and cons of each one? Are they similar or different overall?

    Я начинаю программист на C++, исходящий от Java.

    Мне кажется, что C++-шаблоны и Java-дженерики на самом деле очень похожи. Вы используете их в почти одинаковых случаях и одинаково. И они «чувствуют» то же самое. Однако я могу думать о двух различий между системами:

    В Java вы можете использовать только класс-типов в качестве аргументов типа, в то время как в C++ любого типа подходит. Я не думаю, что это важно, потому что Java-аутобоксинг хорош.

    Более существенная разница: шаблоны C++ являются утиными. Это означает, что вы можете вызывать любую операцию на общем типе T . Если он не поддерживает операцию, возникает ошибка времени компиляции. Однако Java-дженерики не печатаются. По умолчанию все аргументы типа имеют тип Object , и вы можете указать что-то более конкретное по синтаксису T extends Something , что позволяет нам вызвать методы T , определенные в Something .

    Второй момент весьма значителен. Однако, кроме того, каковы различия между этими двумя системами? Каковы плюсы и минусы каждого из них? Являются ли они похожими или разными в целом?

    Создан 09 окт. 14 2014-10-09 09:58:02 Aviv Cohn

    Каковы различия между & ldquo; универсальным & rdquo; типы в C ++ и Java?

    У Java есть дженерики, а C ++ предоставляет очень сильную модель программирования с template с. Итак, в чём же разница между дженериками C ++ и Java?

    9 ответов

    Существует большая разница между ними. В C++ Вы не должны определять класс или интерфейс для универсального типа. Вот почему можно создать действительно родовые функции и классы с протестом более свободного ввода.

    метод выше добавляет два объекта того же типа и может использоваться для любого типа T, который имеет «+» доступный оператор.

    В Java необходимо определить тип, если Вы хотите звонить, методы на объектах передали, что-то как:

    В C++ универсальные функции/классы могут только быть определены в заголовках, так как компилятор генерирует различные функции для различных типов (что это вызывается с). Таким образом, компиляция медленнее. В Java компиляция не имеет крупного штрафа, но Java использует технику, названную «стиранием», где универсальный тип стирается во времени выполнения, таким образом, в Java во время выполнения на самом деле звонит.

    , Таким образом, универсальное программирование в Java не действительно полезно, это — только немного синтаксического сахара для помощи с новой конструкцией foreach.

    РЕДАКТИРОВАНИЕ: мнение выше о полноценности было записано младшим сам. Дженерики Java помогают с безопасностью типов, конечно.

    , Что код является на самом деле неправильным и кроме меньших незначительных сбоев ( template опущенный, синтаксис специализации смотрит по-другому), частичная специализация не делает работа над шаблонами функций, только над шаблонами классов. Код однако работал бы без частичной шаблонной специализации, вместо этого с помощью простой перегрузки:

    Другим преимуществом шаблонов C++ является специализация.

    Теперь при вызове суммы с указателями второй метод назовут при вызове суммы с неуказательными объектами, первый метод назовут, и если Вы будете звонить sum с Special объекты, третье назовут. Я не думаю, что это возможно с Java.

    Java (и C#) дженерики, кажется, простой механизм замены типа выполнения.
    шаблоны C++ являются конструкцией времени компиляции, которые дают Вам способ изменить язык для удовлетворения потребностям. Они — на самом деле просто функциональный язык, который компилятор выполняет во время компиляции.

    C++ имеет шаблоны. Java имеет дженерики, которые выглядят вид вида подобных шаблонов C++, но они очень, очень отличающиеся.

    Шаблонная работа, поскольку имя подразумевает путем обеспечения компилятора (ожидают его. ), шаблон, который это может использовать для генерации безопасного с точки зрения типов кода путем заполнения шаблонных параметров.

    Дженерики, поскольку я понимаю их, работа наоборот: параметры типа используются компилятором, чтобы проверить, что код с помощью них безопасен с точки зрения типов, но получающийся код сгенерирован без типов вообще.

    Думают о шаблонах C++ как действительно хороший макро-система и дженерики Java как инструмент для того, чтобы автоматически генерировать преобразования типа.

    Существует большое объяснение этой темы в Дженерики Java и Наборы Maurice Naftalin, Philip Wadler. Я настоятельно рекомендую эту книгу. Заключить в кавычки:

    Дженерики в Java напоминают шаблоны в C++. Синтаксис сознательно подобен, и семантика сознательно отличаются. Семантически, дженерики Java определяются стиранием, где, поскольку шаблоны C++ определяются расширением.

    считайте полное объяснение здесь .

    Другая функция, что шаблоны C++ имеют тот Java дженерики, не делает специализация. Это позволяет Вам иметь другую реализацию для определенных типов. Таким образом, у Вас может, например, быть высоко оптимизированная версия для интервал , все еще имея универсальную версию для остальной части типов. Или у Вас могут быть различные версии для указателя и неуказательных типов. Это пригождается, если Вы хотите воздействовать на разыменованный объект при вручении указателя.

    Дженерики Java в широком масштабе отличающиеся от шаблонов C++.

    В основном в шаблонах C++ в основном прославленный набор препроцессора/макроса ( Примечание: , так как некоторые люди кажутся не могущими постигать аналогию, я не говорю, что шаблонная обработка является макросом). В Java они — в основном синтаксический сахар для уменьшения шаблонного кастинга Объектов. Вот довольно достойное введение в шаблоны C++ по сравнению с дженериками Java .

    Для разработки по этому вопросу: при использовании шаблона C++ Вы в основном создаете другую копию кода, так же, как если бы Вы использовали #define макрос. Это позволяет Вам делать, вещам нравится, имеют int параметры в шаблонных определениях, которые определяют размеры массивов и такого.

    Java не работает как этот. В Java вся степень объектов от [1 111] java.lang. Объект так, предварительные дженерики, Вы написали бы код как это:

    , потому что все типы набора Java используемый Объект как их базовый тип, таким образом, Вы могли поместить что-либо в них. Java 5 катается вокруг и добавляет дженерики, таким образом, можно сделать вещи как:

    И это — все Дженерики Java: обертки для кастинга объектов. Поэтому Дженерики Java не усовершенствованы. Они используют стирание типа. Это решение было принято, потому что Дженерики Java пришли настолько поздно в части, что они не хотели повреждаться, обратная совместимость ( Map применимо каждый раз, когда Map требуется). Сравните это с.Net/C#, где стирание типа не используется, который приводит ко всем видам различий (например, можно использовать типы примитивов и IEnumerable и IEnumerable перенос никакое отношение друг к другу).

    И класс с помощью дженериков, скомпилированных с Java 5 +, компилятор применим на JDK 1.4 (предполагающий, что он не использует никакие другие функции или классы, которые требуют Java 5 +).

    Вот почему Дженерики Java называют синтаксический сахар .

    , Но это решение о том, как сделать, дженерики имеют сильные воздействия так так, чтобы (превосходный) дженерики FAQ Java возникли для ответа на многих, многие люди вопросов имеют о Дженериках Java.

    шаблоны C++ имеют много функций, которые не делают Дженерики Java:

    Использование аргументов типа примитива.

    Java не позволяет использование аргументов типа примитива в дженериках.

  • Java позволяет ограничивать аргументов.
  • действительно нужно подчеркнуть, что шаблонные вызовы с различными аргументами действительно являются различными типами. Они даже не совместно используют статических участников. В Java дело обстоит не так.

    Кроме различий с дженериками, для полноты, вот основное сравнение C++ и Java (и другой ).

    И я могу также предложить Взгляды в Java. Как программист на C++ много понятий как объекты уже будет второй натурой, но существуют тонкие различия, таким образом, может стоить иметь вводный текст, даже если Вы просматриваете части.

    Многое из того, что Вы изучите при изучении Java, является всеми библиотеками (и стандарт — что прибывает в JDK — и нестандартный, который включает наиболее часто используемые вещи как Spring). Синтаксис Java является более подробным, чем синтаксис C++ и не имеет большого количества функций C++ (например, перегрузка оператора, множественное наследование, механизм деструктора, и т.д.), но это строго не делает это подмножеством C++ также.

    habrahabr Каковы различия между Generics в C#и Java. и шаблонами в C++?

    java generics habrahabr (11)

    Последующие действия по сравнению с предыдущей публикацией.

    Шаблоны — одна из основных причин, по которым C ++ терпит неудачу так сильно в intellisense, независимо от используемой IDE. Из-за специализации шаблона среда IDE никогда не может быть действительно уверена, существует ли данный член или нет. Рассматривать:

    Теперь, курсор находится в указанной позиции, и для этой IDE сложно сказать, если и что у членов a . Для других языков синтаксический анализ был бы прост, но для C ++ требуется довольно немного оценки.

    Становится хуже. Что, если my_int_type были определены внутри шаблона класса? Теперь его тип будет зависеть от аргумента другого типа. И здесь даже компиляторы терпят неудачу.

    После некоторого раздумья программист сделал вывод, что этот код такой же, как и выше: Y ::my_type разрешает int , поэтому b должен быть того же типа, что и a , правильно?

    Неправильно. В момент, когда компилятор пытается разрешить этот оператор, он еще не знает Y ::my_type ! Поэтому он не знает, что это тип. Это может быть что-то другое, например, функция-член или поле. Это может привести к двусмысленности (хотя и не в данном случае), поэтому компилятор терпит неудачу. Мы должны прямо сказать, что мы ссылаемся на имя типа:

    Теперь код компилируется. Чтобы узнать, как возникают неясности из этой ситуации, рассмотрите следующий код:

    Этот оператор кода отлично действует и говорит C ++ выполнить вызов функции Y ::my_type . Однако, если my_type не является функцией, а скорее типом, этот оператор все равно будет действительным и выполняет специальный листинг (приведение в стиле функции), который часто является вызовом конструктора. Компилятор не может сказать, что мы имеем в виду, поэтому мы должны устранить эту проблему.

    В основном я использую Java, а дженерики — относительно новые. Я продолжаю читать, что Java приняла неправильное решение или что .NET имеет лучшие реализации и т. Д. И т. Д.

    Мастер Йода рекомендует:  Провайдер Sipnet стал поставщиком VoIP-услуг для пользователей Mail.Ru Агент

    Итак, каковы основные различия между C ++, C #, Java в дженериках? Плюсы / минусы каждого?

    Уже есть много хороших ответов на то, каковы различия, поэтому позвольте мне дать немного другую точку зрения и добавить, почему .

    Как уже объяснялось, основным отличием является стирание типа , то есть тот факт, что компилятор Java стирает общие типы, и они не попадают в сгенерированный байт-код. Однако возникает вопрос: зачем кому-то это делать? Это не имеет смысла! Или это?

    Ну, а какая альтернатива? Если вы не реализуете дженерики на языке, где вы их реализуете? И ответ: в виртуальной машине. Что нарушает совместимость.

    С другой стороны, стирание стилей позволяет смешивать общие клиенты с не-генерическими библиотеками. Другими словами: код, который был скомпилирован на Java 5, все еще может быть развернут в Java 1.4.

    Однако Microsoft решила отказаться от совместимости для дженериков. Вот почему .NET Generics «лучше», чем Java Generics.

    Конечно, Солнце не идиоты или трусы. Причина, по которой они «выкидывались», заключалась в том, что Java была значительно старше и более распространена, чем .NET, когда они вводили дженерики. (Они были представлены примерно в то же время в обоих мирах.) Нарушение обратной совместимости было бы огромной болью.

    Еще один способ: в Java, Generics являются частью языка (что означает, что они применяются только к Java, а не к другим языкам), в .NET они являются частью виртуальной машины (что означает, что они применяются ко всем языкам, а не просто C # и Visual Basic.NET).

    Сравните это с функциями .NET, такими как LINQ, лямбда-выражения, локальные переменные типа, анонимные типы и деревья выражений: все это языковые функции. Вот почему существуют тонкие различия между VB.NET и C #: если эти функции были частью VM, они были бы одинаковыми на всех языках. Но CLR не изменился: в .NET 3.5 SP1 он все тот же, как и в .NET 2.0. Вы можете скомпилировать программу на C #, которая использует LINQ с компилятором .NET 3.5 и все еще запускать ее на .NET 2.0 при условии, что вы не используете библиотеки .NET 3.5. Это не сработает с generics и .NET 1.1, но оно будет работать с Java и Java 1.4.

    В Википедии есть отличные рецензии, сравнивающие как генераторы Java / C #, так и шаблоны Java generics / C ++ . Основная статья о Generics кажется немного загроможденной, но в ней есть хорошая информация.

    И Java, и C # представили дженерики после их первого выпуска языка. Тем не менее, существуют различия в том, как изменились основные библиотеки при введении дженериков. Генераторы C # — это не просто магия компилятора, и поэтому невозможно было создать существующие классы библиотеки, не нарушая совместимость с предыдущими версиями .

    Например, в Java существующая структура коллекций была полностью обобщена . В Java нет как универсальной, так и старой версии классов коллекций. В некотором смысле это намного чище — если вам нужно использовать коллекцию на C #, есть очень мало оснований идти с не-универсальной версией, но эти унаследованные классы остаются на месте, загромождая пейзаж.

    Другим заметным отличием являются классы Enum в Java и C #. Java Enum имеет это несколько извилистое определение:


    (см. очень ясное объяснение Анжелики Лангер, почему именно так. По сути, это означает, что Java может предоставить безопасный доступ типа от строки до значения Enum:

    Сравните это с версией C #:

    Поскольку Enum уже существовал на C #, прежде чем дженерики были введены в язык, определение не могло измениться без нарушения существующего кода. Таким образом, подобно коллекциям, он остается в основных библиотеках в этом устаревшем состоянии.

    11 месяцев поздно, но я думаю, что этот вопрос готов к некоторым материалам Java Wildcard.

    Это синтаксическая особенность Java. Предположим, у вас есть метод:

    И предположим, что вам не нужно ссылаться на тип T в теле метода. Вы объявляете имя T, а затем используете его только один раз, так почему вы должны думать о имени для него? Вместо этого вы можете написать:

    Вопросительный знак просит компилятор сделать вид, что вы объявили нормальный именованный тип, который должен появиться только один раз в этом месте.

    Вы ничего не можете сделать с помощью подстановочных знаков, которые вы также не можете использовать с параметром named type (как это всегда делается на C ++ и C #).

    Сам Андерс Хейлсберг рассказал о различиях здесь « Дженерики в C #, Java и C ++ ».

    В Java дженерики — это только уровень компилятора, поэтому вы получаете:

    Обратите внимание, что тип ‘a’ — это список массивов, а не список строк. Таким образом, тип списка бананов будет равен () списку обезьян.

    Самая большая жалоба — стирание типа. При этом дженерики не применяются во время выполнения. Вот ссылка на некоторые документы Sun по этому вопросу .

    Генерики реализуются с помощью стирания типа: информация об общем типе присутствует только во время компиляции, после чего она удаляется компилятором.

    Похоже, что среди других очень интересных предложений есть один о совершенствовании дженериков и отмене обратной совместимости:

    В настоящее время генерические средства реализуются с использованием стирания, что означает, что общая информация типа недоступна во время выполнения, что затрудняет запись какого-либо кода. Эти средства были реализованы таким образом, чтобы поддерживать обратную совместимость со старым нестандартным кодом. Обоснованные генерические файлы будут предоставлять информацию об общем типе во время выполнения, что приведет к потере устаревшего не общего кода. Тем не менее, Нил Гафтер предложил сделать типы повторно доступными только в том случае, если они указаны, чтобы не нарушить обратную совместимость.

    Шаблоны C ++ на самом деле намного мощнее, чем их C # и Java-аналоги, поскольку они оцениваются во время компиляции и поддерживают специализацию. Это позволяет метапрограммирование шаблонов и делает компилятор C ++ эквивалентным машине Тьюринга (т.е. во время процесса компиляции вы можете вычислить все, что можно вычислить с помощью машины Тьюринга).

    NB: У меня недостаточно комментариев для комментариев, поэтому не стесняйтесь переместить это как комментарий к соответствующему ответу.

    Вопреки распространенному мнению, который я никогда не понимаю, откуда он пришел, .NET реализовал настоящие генерики, не нарушая обратной совместимости, и они потратили на это явное усилие. Вам не нужно менять свой нестандартный код .net 1.0 на generics, который будет использоваться в .net 2.0. Как общие, так и не общие списки по-прежнему доступны в .NET Framework 2.0 даже до версии 4.0, точно для чего-то другого, кроме соображений обратной совместимости. Поэтому старые коды, которые все еще используют не общий ArrayList, будут по-прежнему работать и использовать тот же класс ArrayList, что и раньше. Совместимость с обратным кодом всегда поддерживается с 1.0 до сих пор . Так что даже в .net 4.0 вам все равно придется использовать любой класс не-generics из 1.0 BCL, если вы решите это сделать.

    Поэтому я не думаю, что java должен нарушить обратную совместимость для поддержки истинных дженериков.

    Каковы различия между Generics в C#и Java. и шаблонами в C++?

    generic java (11)

    Сам Андерс Хейлсберг рассказал о различиях здесь « Дженерики в C #, Java и C ++ ».

    В основном я использую Java, а дженерики — относительно новые. Я продолжаю читать, что Java приняла неправильное решение или что .NET имеет лучшие реализации и т. Д. И т. Д.

    Итак, каковы основные различия между C ++, C #, Java в дженериках? Плюсы / минусы каждого?

    Я добавлю свой голос к шуму и сделаю удар, чтобы прояснить ситуацию:

    C # Generics позволяет объявить что-то вроде этого.

    и тогда компилятор помешает вам помещать в список вещи, которые не являются Person .
    За кулисами компилятор C # просто кладет List

    в DLL-файл .NET, но во время выполнения компилятор JIT идет и создает новый набор кода, как если бы вы написали специальный класс списка только для того, чтобы содержать людей — что-то как ListOfPerson .

    Преимущество этого в том, что он делает это очень быстро. Там нет кастинга или каких-либо других вещей, и поскольку в dll содержится информация о том, что это список людей, другой код, который смотрит на него позже, используя отражение, может сказать, что он содержит объекты Person (поэтому вы получаете intellisense и т. Д.).

    Недостатком этого является то, что старый код C # 1.0 и 1.1 (до того, как они добавили генерики) не понимает этот новый List , поэтому вам нужно вручную преобразовать данные обратно в обычный старый List чтобы взаимодействовать с ними. Это не проблема, потому что двоичный код C # 2.0 не совместим с предыдущим. Единственный раз, когда это произойдет, — если вы обновляете старый код C # 1.0 / 1.1 до C # 2.0

    Java Generics позволяет вам объявить что-то вроде этого.

    На поверхности он выглядит одинаково, и это своего рода. Компилятор также не позволит вам помещать в список вещи, которые не являются Person .

    Разница в том, что происходит за кулисами. В отличие от C #, Java не идет и не создает специальный ListOfPerson — он просто использует простой старый ArrayList который всегда был на Java. Когда вы получаете вещи из массива, обычный Person p = (Person)foo.get(1); кастинг-танец еще предстоит сделать. Компилятор сохранит вам нажатия клавиш, но скорость попадания / кастинга по-прежнему поступила так же, как и всегда.
    Когда люди упоминают «Type Erasure», это то, о чем они говорят. Компилятор вставляет вам броски, а затем «стирает» тот факт, что он должен быть списком Person не только Object

    Преимущество такого подхода заключается в том, что старый код, который не понимает дженериков, не нуждается в заботе. Он по-прежнему имеет дело с тем же старым ArrayList что и всегда. Это более важно в java-мире, потому что они хотели поддерживать компиляцию кода с использованием Java 5 с помощью дженериков, а его запуск на старых 1,4 или предыдущих JVM, которые Microsoft намеренно решила не беспокоиться.

    Недостатком является упоминание о скорости, о которой я упомянул ранее, а также о том, что псевдокласс класса ListOfPerson отсутствует или что-либо подобное, ListOfPerson в файлы .class, код, который смотрит на него позже (с отражением или если вы вытаскиваете его из другого коллекция, где она была преобразована в Object или так далее) никак не может сказать, что она предназначена для списка, содержащего только Person а не только любой другой список массивов.

    Шаблоны C ++ позволяют объявлять что-то вроде этого

    Это похоже на C # и Java generics, и он будет делать то, что, по вашему мнению, должен делать, но за кулисами происходят разные вещи.

    Он имеет больше всего общего с C # generics тем, что он создает специальные pseudo-classes а не просто выбрасывает информацию о типе, как java, но это совершенно другой чайник.

    Оба C # и Java производят вывод, который предназначен для виртуальных машин. Если вы напишете код, в котором есть класс Person , в обоих случаях некоторая информация о классе Person войдет в файл .dll или .class, и JVM / CLR сделает это с этим.

    C ++ создает необработанный двоичный код x86. Все не является объектом, и нет никакой базовой виртуальной машины, которая должна знать о классе Person . Нет бокса или unboxing, и функции не должны принадлежать к классам или вообще что-либо.

    Из-за этого компилятор C ++ не накладывает ограничений на то, что вы можете делать с шаблонами — в основном любой код, который вы могли бы написать вручную, вы можете получить шаблоны для записи для вас.
    Наиболее очевидным примером является добавление вещей:

    В C # и Java система generics должна знать, какие методы доступны для класса, и нужно передать это на виртуальную машину. Единственный способ сказать это — либо жестко кодировать фактический класс, либо использовать интерфейсы. Например:

    Этот код не будет компилироваться на C # или Java, потому что он не знает, что тип T фактически предоставляет метод под названием Name (). Вы должны сказать это — в C #, как это:

    И тогда вы должны убедиться, что вещи, которые вы передаете addNames, реализуют интерфейс IHasName и т. Д. Синтаксис java отличается ( ), но он испытывает те же проблемы.

    «Классическим» случаем для этой проблемы является попытка написать функцию, которая делает это

    Вы не можете написать этот код, потому что нет способов объявить интерфейс с помощью метода + . Ты облажался.

    C ++ не страдает ни от одной из этих проблем. Компилятор не заботится о передаче типов к любой VM — если оба объекта имеют функцию .Name (), они будут скомпилированы. Если они этого не сделают, это не произойдет. Просто.

    Итак, у вас есть это 🙂

    Шаблоны C ++ на самом деле намного мощнее, чем их C # и Java-аналоги, поскольку они оцениваются во время компиляции и поддерживают специализацию. Это позволяет метапрограммирование шаблонов и делает компилятор C ++ эквивалентным машине Тьюринга (т.е. во время процесса компиляции вы можете вычислить все, что можно вычислить с помощью машины Тьюринга).

    11 месяцев поздно, но я думаю, что этот вопрос готов к некоторым материалам Java Wildcard.

    Это синтаксическая особенность Java. Предположим, у вас есть метод:

    И предположим, что вам не нужно ссылаться на тип T в теле метода. Вы объявляете имя T, а затем используете его только один раз, так почему вы должны думать о имени для него? Вместо этого вы можете написать:

    Вопросительный знак просит компилятор сделать вид, что вы объявили нормальный именованный тип, который должен появиться только один раз в этом месте.

    Вы ничего не можете сделать с помощью подстановочных знаков, которые вы также не можете использовать с параметром named type (как это всегда делается на C ++ и C #).

    Уже есть много хороших ответов на то, каковы различия, поэтому позвольте мне дать немного другую точку зрения и добавить, почему .

    Как уже объяснялось, основным отличием является стирание типа , то есть тот факт, что компилятор Java стирает общие типы, и они не попадают в сгенерированный байт-код. Однако возникает вопрос: зачем кому-то это делать? Это не имеет смысла! Или это?

    Ну, а какая альтернатива? Если вы не реализуете дженерики на языке, где вы их реализуете? И ответ: в виртуальной машине. Что нарушает совместимость.

    С другой стороны, стирание стилей позволяет смешивать общие клиенты с не-генерическими библиотеками. Другими словами: код, который был скомпилирован на Java 5, все еще может быть развернут в Java 1.4.

    Однако Microsoft решила отказаться от совместимости для дженериков. Вот почему .NET Generics «лучше», чем Java Generics.

    Конечно, Солнце не идиоты или трусы. Причина, по которой они «выкидывались», заключалась в том, что Java была значительно старше и более распространена, чем .NET, когда они вводили дженерики. (Они были представлены примерно в то же время в обоих мирах.) Нарушение обратной совместимости было бы огромной болью.

    Еще один способ: в Java, Generics являются частью языка (что означает, что они применяются только к Java, а не к другим языкам), в .NET они являются частью виртуальной машины (что означает, что они применяются ко всем языкам, а не просто C # и Visual Basic.NET).

    Сравните это с функциями .NET, такими как LINQ, лямбда-выражения, локальные переменные типа, анонимные типы и деревья выражений: все это языковые функции. Вот почему существуют тонкие различия между VB.NET и C #: если эти функции были частью VM, они были бы одинаковыми на всех языках. Но CLR не изменился: в .NET 3.5 SP1 он все тот же, как и в .NET 2.0. Вы можете скомпилировать программу на C #, которая использует LINQ с компилятором .NET 3.5 и все еще запускать ее на .NET 2.0 при условии, что вы не используете библиотеки .NET 3.5. Это не сработает с generics и .NET 1.1, но оно будет работать с Java и Java 1.4.

    Самая большая жалоба — стирание типа. При этом дженерики не применяются во время выполнения. Вот ссылка на некоторые документы Sun по этому вопросу .

    Генерики реализуются с помощью стирания типа: информация об общем типе присутствует только во время компиляции, после чего она удаляется компилятором.

    В Википедии есть отличные рецензии, сравнивающие как генераторы Java / C #, так и шаблоны Java generics / C ++ . Основная статья о Generics кажется немного загроможденной, но в ней есть хорошая информация.

    Похоже, что среди других очень интересных предложений есть один о совершенствовании дженериков и отмене обратной совместимости:

    В настоящее время генерические средства реализуются с использованием стирания, что означает, что общая информация типа недоступна во время выполнения, что затрудняет запись какого-либо кода. Эти средства были реализованы таким образом, чтобы поддерживать обратную совместимость со старым нестандартным кодом. Обоснованные генерические файлы будут предоставлять информацию об общем типе во время выполнения, что приведет к потере устаревшего не общего кода. Тем не менее, Нил Гафтер предложил сделать типы повторно доступными только в том случае, если они указаны, чтобы не нарушить обратную совместимость.

    C ++ редко использует терминологию «generics». Вместо этого используется слово «шаблоны» и более точно. Шаблоны описывают один метод для достижения общего дизайна.

    Шаблоны C ++ сильно отличаются от того, что реализуют как C #, так и Java по двум основным причинам. Первая причина заключается в том, что шаблоны C ++ не только допускают аргументы типа компиляции, но и аргументы const-value для компиляции: шаблоны могут быть заданы как целые числа, так и даже сигнатуры функций. Это означает, что во время компиляции вы можете делать довольно забавные вещи, например, вычисления:

    Этот код также использует другую отличительную особенность шаблонов C ++, а именно специализированную специализацию. Код определяет один шаблон класса, product с одним аргументом значения. Он также определяет специализацию для этого шаблона, которая используется всякий раз, когда аргумент оценивается в 1. Это позволяет мне определить рекурсию над определениями шаблонов. Я считаю, что это было впервые обнаружено Андреем Александреску .

    Специализация шаблона важна для C ++, поскольку она допускает структурные различия в структурах данных. Шаблоны в целом являются средством объединения интерфейса между типами. Однако, хотя это желательно, все типы не могут рассматриваться одинаково внутри реализации. Шаблоны C ++ учитывают это. Это очень та же разница, что и ООП между интерфейсом и реализацией с переопределением виртуальных методов.

    Шаблоны C ++ необходимы для его алгоритмической парадигмы программирования. Например, почти все алгоритмы для контейнеров определяются как функции, которые принимают тип контейнера в качестве типа шаблона и обрабатывают их равномерно. Собственно, это не совсем правильно: C ++ не работает с контейнерами, а скорее на диапазонах , которые определены двумя итераторами, указывая на начало и конец конца контейнера. Таким образом, все содержимое ограничено итераторами: begin

    В Java дженерики — это только уровень компилятора, поэтому вы получаете:

    Обратите внимание, что тип ‘a’ — это список массивов, а не список строк. Таким образом, тип списка бананов будет равен () списку обезьян.

    И Java, и C # представили дженерики после их первого выпуска языка. Тем не менее, существуют различия в том, как изменились основные библиотеки при введении дженериков. Генераторы C # — это не просто магия компилятора, и поэтому невозможно было создать существующие классы библиотеки, не нарушая совместимость с предыдущими версиями .

    Например, в Java существующая структура коллекций была полностью обобщена . В Java нет как универсальной, так и старой версии классов коллекций. В некотором смысле это намного чище — если вам нужно использовать коллекцию на C #, есть очень мало оснований идти с не-универсальной версией, но эти унаследованные классы остаются на месте, загромождая пейзаж.

    Другим заметным отличием являются классы Enum в Java и C #. Java Enum имеет это несколько извилистое определение:

    (см. очень ясное объяснение Анжелики Лангер, почему именно так. По сути, это означает, что Java может предоставить безопасный доступ типа от строки до значения Enum:

    Сравните это с версией C #:

    Поскольку Enum уже существовал на C #, прежде чем дженерики были введены в язык, определение не могло измениться без нарушения существующего кода. Таким образом, подобно коллекциям, он остается в основных библиотеках в этом устаревшем состоянии.

    Добавить комментарий