Каковы преимущества стирания типов в Java?

98

Сегодня я прочитал твит, в котором говорилось:

Забавно, когда пользователи Java жалуются на стирание типа, единственное, что в Java сделала правильно, игнорируя все ошибки.

Таким образом, мой вопрос:

Есть ли преимущества от стирания типов в Java? Какие технические преимущества или преимущества стиля программирования он (возможно) предлагает помимо предпочтения реализаций JVM для обратной совместимости и производительности во время выполнения?

вертти
источник
6
Если вы не хотите, чтобы ваш вопрос был закрыт как «основанный на мнении», вам следует задать его иначе. (Например, «каковы технические преимущества стирания шрифтов»). Еще лучше спросите твитера, что он на самом деле говорит.
Stephen C
3
Уберите "почему это хорошо". «Хорошо» является явно субъективной характеристикой, и (очевидно) возникает вопрос , что тип стирание является хорошей вещью ... которая является спорной.
Stephen C
3
Да поможет нам Бог, если этот сайт превратится в вопросы обо всем, что когда-либо публиковалось в Твиттере. По крайней мере, укажите на свой вопрос авторитетный источник.
Marquis of Lorne
21
Почему вопрос должен быть из «авторитетного источника»? Это просто глупо.
vertti 04
4
Что ж, большинство проблем здесь не из ЛЮБОГО источника, за исключением собственных проблем спрашивающих с конкретным программным обеспечением / языком / еще много чего, и, к счастью, есть много профессионалов, которые «тратят» свое время, помогая этим людям.
vertti 04

Ответы:

90

Тип Стирание - это хорошо

Будем придерживаться фактов

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

Забавно, когда пользователи Java жалуются на стирание типа, единственное, что в Java сделала правильно, игнорируя все ошибки.

Я получаю огромные выгоды (например, параметричность) и нулевую стоимость (предполагаемая стоимость - это предел воображения).

new T - это неработающая программа. Это изоморфно утверждению «все предложения истинны». Я не особо в этом разбираюсь.

Цель: разумные программы

Эти твиты отражают точку зрения, которая не интересует, можем ли мы заставить машину что- то делать , а больше, можем ли мы рассуждать, что машина будет делать то, что мы действительно хотим. Хорошая аргументация - доказательство. Доказательства могут быть указаны в формальных обозначениях или в чем-то менее формальном. Независимо от языка спецификации, они должны быть четкими и строгими. Неформальные спецификации не невозможно правильно структурировать, но они часто бывают ошибочными в практическом программировании. Мы заканчиваем исправлениями, такими как автоматические и исследовательские тесты, чтобы исправить проблемы, которые у нас возникают с неформальными рассуждениями. Это не означает, что тестирование по своей сути является плохой идеей, но процитированный пользователь Twitter предполагает, что есть гораздо лучший способ.

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

Использование систем типов для рассуждений

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

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

Карри-Ховард прекрасно подходит для того, что мы хотели бы делать со спецификациями для программы.

Полезны ли системы типов в Java?

Даже с пониманием Карри-Ховарда некоторым людям легко не принимать во внимание ценность системы типов, когда она

  1. очень сложно работать с
  2. соответствует (через Карри-Ховарда) логике с ограниченной выразительностью
  3. нарушена (что позволяет характеризовать системы как «слабые» или «сильные»).

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

Что касается второго пункта, Java почти соответствует логике первого порядка. Обобщения дают возможность использовать систему типов, эквивалентную универсальной количественной оценке. К сожалению, подстановочные знаки дают нам лишь небольшую часть экзистенциальной количественной оценки. Но универсальная количественная оценка - хорошее начало. Приятно иметь возможность сказать, что функции для List<A>работы универсально для всех возможных списков, потому что A полностью не ограничен. Это приводит к тому, о чем пользователь Twitter говорит о «параметричности».

Часто цитируемая статья о параметричности - это бесплатные теоремы Филипа Вадлера ! . Что интересно в этой статье, так это то, что с помощью одной только сигнатуры типа мы можем доказать некоторые очень интересные инварианты. Если бы нам пришлось писать автоматические тесты для этих инвариантов, мы бы очень зря теряли время. Например, для List<A>, только из сигнатуры типа дляflatten

<A> List<A> flatten(List<List<A>> nestedLists);

мы можем рассуждать, что

flatten(nestedList.map(l -> l.map(any_function)))
     flatten(nestList).map(any_function)

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

Отсутствие стирания может привести к злоупотреблениям

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

  • побочные эффекты с внешней системой
  • отражение

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

Так как же могут быть «полезными» нестертые дженерики? Давайте рассмотрим использование, упомянутое в твите:

<T> T broken { return new T(); }

Что произойдет, если у T нет конструктора без аргументов? На некоторых языках вы получаете нулевое значение. Или, возможно, вы пропустите нулевое значение и сразу перейдете к возбуждению исключения (к чему все равно приводят нулевые значения). Поскольку наш язык является полным по Тьюрингу, невозможно понять, какие вызовы brokenбудут включать «безопасные» типы с конструкторами без аргументов, а какие - нет. Мы потеряли уверенность в том, что наша программа работает повсеместно.

Стирание означает, что мы рассудили (так что давайте стираем)

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

Стирание побуждает к рассуждению.

Сукант Хаджра
источник
7
За время, которое я потратил, чтобы собрать этот ответ, некоторые другие люди ответили схожими ответами. Но, написав это много, я решил выложить это, а не выбросить.
Sukant Hajra
32
Что касается стирания типов, то это была наполовину тщетная попытка добавить в Java функцию без нарушения обратной совместимости. Это не сила. Если основная проблема заключалась в том, что вы можете попытаться создать экземпляр объекта без конструктора без параметров, правильный ответ - разрешить ограничение типа «типы с конструкторами без параметров», чтобы не нарушать языковые функции.
Basic
5
Базовый, с уважением, вы не приводите связных аргументов. Вы просто утверждаете, что стирание «наносит вред», полностью игнорируя ценность доказательств времени компиляции как убедительного инструмента для рассуждений о программах. Более того, эти преимущества полностью ортогональны извилистому пути и мотивации, которую Java выбрала для реализации дженериков. Кроме того, эти преимущества относятся к языкам с гораздо более продуманной реализацией стирания.
Sukant Hajra 01
44
Аргумент, представленный здесь, не против стирания, он против отражения (и проверки типов во время выполнения в целом) - он в основном утверждает, что отражение - это плохо, поэтому тот факт, что стирание не работает с ними, хорошо. Это само по себе обоснованное мнение (хотя многие люди не согласятся); но это не имеет особого смысла в контексте, потому что в Java уже есть проверки отражения / выполнения, а они не исчезли и не исчезнут. Поэтому наличие конструкции, которая не взаимодействует с существующей, не устаревшей частью языка, является ограничением, независимо от того, как вы на это смотрите.
Павел Минаев
10
Ваш ответ просто не по теме. Вы вводите длинное (хотя и интересное само по себе) объяснение того, почему стирание типов как абстрактная концепция обычно полезно при разработке систем типов, но тема - это преимущество конкретной характеристики Java Generics, обозначенной как «стирание типов» ", который лишь внешне напоминает описываемую вами концепцию, полностью не в состоянии предоставить интересные инварианты и ввести необоснованные ограничения.
Марко Топольник
41

Типы - это конструкция, используемая для написания программ таким образом, чтобы компилятор мог проверять правильность программы. Тип - это предложение для значения - компилятор проверяет, что это утверждение истинно.

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

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

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

Стирание важно для сохранения свойства «параметричности» типа данных. Скажем, у меня есть тип «Список», параметризованный по типу компонента T. т.е. List <T>. Этот тип является утверждением, что этот тип List работает одинаково для любого типа T. Тот факт, что T является абстрактным, неограниченным параметром типа, означает, что мы ничего не знаем об этом типе, поэтому нам запрещено делать что-либо особенное для особых случаев T.

например, у меня есть список xs = asList ("3"). Добавляю элемент: xs.add ("q"). Я получаю ["3", "q"]. Поскольку это параметрический параметр, я могу предположить, что List xs = asList (7); xs.add (8) заканчивается [7,8]. Я знаю по типу, что он ничего не делает для String и ничего для Int.

Более того, я знаю, что функция List.add не может изобретать значения T из воздуха. Я знаю, что если к моему asList («3») добавить «7», единственные возможные ответы будут построены из значений «3» и «7». Нет возможности добавления «2» или «z» в список, потому что функция не сможет его построить. Ни одно из этих других значений не имеет смысла добавлять, а параметричность предотвращает создание этих некорректных программ.

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

techtangents
источник
15

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


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

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

Стирание типов в Java не позволяет добиться этого - оно наносит вред компилятору, как в этом примере:

void doStuff(List<Integer> collection) { 
}

void doStuff(List<String> collection) // ERROR: a method cannot have 
                   // overloads which only differ in type parameters

(Два приведенных выше объявления после стирания превращаются в одну и ту же сигнатуру метода.)

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

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

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

(Необходимо extends Objectбыло добавить дубликат, чтобы сохранить обратную совместимость стертой подписи.)

Имея это в виду, давайте вернемся к цитате:

Забавно, когда пользователи Java жалуются на стирание типа, единственное, что у Java есть

Что именно сделала Java? Само ли это слово, независимо от значения? Для контраста взгляните на скромный intтип: проверка типа во время выполнения никогда не выполняется и даже не возможна, а выполнение всегда полностью типобезопасно. Вот как выглядит стирание шрифта, когда все сделано правильно: вы даже не знаете, что оно там есть.

Марко Топольник
источник
10

Единственное, что я здесь не рассматриваю, - это то, что полиморфизм времени выполнения ООП фундаментально зависит от реификации типов во время выполнения. Когда язык, основу которого составляют реферированные типы, вносит существенное расширение в свою систему типов и основывает его на стирании типов, когнитивный диссонанс является неизбежным результатом. Именно это произошло с сообществом Java; именно поэтому стирание типов вызвало столько споров, и, в конечном итоге, есть планы по их отмене в будущих версиях Java . Находить что-то смешное в этой жалобе пользователей Java - либо откровенное непонимание духа Java, либо сознательно осуждающая шутка.

Утверждение «стирание - единственное, что сделала Java правильно» подразумевает утверждение, что «все языки, основанные на динамической диспетчеризации аргумента функции типа времени выполнения, в корне ошибочны». Хотя само по себе утверждение, безусловно, является законным и может даже рассматриваться как обоснованная критика всех языков ООП, включая Java, оно не может выступать в качестве ключевой точки для оценки и критики функций в контексте Java , где полиморфизм времени выполнения аксиоматичен.

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



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

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

Марко Топольник
источник
Спасибо за это. Это именно то, что я ожидал увидеть при ответе на этот вопрос.
Дэниел Лэнгдон
Динамическая отправка абсолютно не зависит от овеществленных типов. Он часто полагается на то, что в литературе по программированию называют «тегами», но даже в этом случае теги использовать не обязательно. Если вы создаете интерфейс (для объекта) и создаете анонимные экземпляры этого интерфейса, то вы выполняете динамическую отправку без необходимости в повторных типах вообще.
Sukant Hajra
@sukanthajra Это просто секущиеся волосы. Тег - это информация времени выполнения, необходимая для определения типа поведения, которое демонстрирует экземпляр. Это вне досягаемости системы статических типов, и в этом суть обсуждаемой концепции.
Марко Топольник
Существует очень статичный тип, разработанный именно для этого рассуждения, который называется «тип суммы». Он также называется «вариантным типом» (в очень хорошей книге Бенджамина Пирса «Типы и языки программирования»). Так что это вообще не секущиеся волосы. Кроме того, вы можете использовать катаморфизм / складку / посетителя для полностью без тегов подхода.
Sukant Hajra
Спасибо за урок теории, но не вижу актуальности. Наша тема - система типов Java.
Марко Топольник
9

Последующее сообщение того же пользователя в той же беседе:

new T - это неработающая программа. Это изоморфно утверждению «все предложения истинны». Я не особо в этом разбираюсь.

(Это было сделано в ответ на заявление другого пользователя, а именно , что «кажется , в некоторых ситуациях„новый Т“будет лучше», идея в том , что new T()невозможно из - за типа стирания (Это спорно. - даже если Tбыли доступны времени выполнения, это может быть абстрактный класс или интерфейс, или он может быть Void, или он может не иметь конструктора без аргументов, или его конструктор без аргументов может быть частным (например, потому что он должен быть одноэлементным классом), или его Конструктор no-arg может указать проверенное исключение, которое общий метод не перехватывает и не определяет, но это было предпосылкой. Тем не менее T.class.newInstance(), верно, что без стирания вы могли бы хотя бы написать , что решает эти проблемы.))

Эта точка зрения, согласно которой типы изоморфны предложениям, предполагает, что пользователь имеет опыт работы в формальной теории типов. (S) ему, скорее всего, не нравятся «динамические типы» или «типы времени выполнения», и он предпочел бы Java без отрицательных значений, instanceofи отражения, и так далее. (Подумайте о таком языке, как Standard ML, который имеет очень богатую (статическую) систему типов и чья динамическая семантика вообще не зависит от какой-либо информации о типах.)

Между прочим, стоит иметь в виду, что пользователь троллит: хотя он, вероятно, искренне предпочитает (статически) типизированные языки, он не пытается искренне убедить других в этой точке зрения. Скорее, основная цель исходного твита состояла в том, чтобы высмеять тех, кто не согласен, и после того, как некоторые из этих несогласных вмешались, пользователь опубликовал последующие твиты, такие как «Причина стирания типа в Java заключается в том, что Вадлер и другие знают, что они делают, в отличие от пользователей java ". К сожалению, из-за этого трудно понять, о чем он думает на самом деле; но, к счастью, это также, вероятно, означает, что это не очень важно. Люди, у которых есть реальная глубина своих взглядов, обычно не прибегают к троллям, лишенным содержания.

руах
источник
2
Он не компилируется на Java, потому что имеет стирание типа
Марк Роттевил
1
@MarkRotteveel: Спасибо; Я добавил параграф, чтобы объяснить (и прокомментировать) это.
ruakh 04
1
Судя по сочетанию голосов за и против, это самый противоречивый ответ, который я когда-либо публиковал. Я странно горжусь.
ruakh
6

Хорошо то, что при появлении дженериков не было необходимости изменять JVM. Java реализует универсальные шаблоны только на уровне компилятора.

Евгений Дорофеев
источник
1
JVM меняется по сравнению с чем? Шаблоны также не потребовали бы изменений JVM.
Marquis of Lorne
5

Стирание шрифтов - это хорошо, потому что то, что оно делает невозможным, вредно. Предотвращение проверки аргументов типа во время выполнения упрощает понимание программ и рассуждения о них.

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

public List<Integer> XXX(final List<Integer> l);

Каковы возможные реализации этой функции? Очень много. Вы можете очень мало рассказать о том, что делает эта функция. Это могло быть изменение списка ввода. Это может быть объединение целых чисел в пары, их суммирование и возвращение списка вдвое меньшего размера. Есть много других возможностей, которые можно вообразить. Теперь рассмотрим:

public <T> List<T> XXX(final List<T> l);

Сколько существует реализаций этой функции? Поскольку реализация не может знать тип элементов, теперь можно исключить огромное количество реализаций: элементы нельзя комбинировать, добавлять в список или фильтровать и т. Д. Мы ограничены такими вещами, как: идентификация (без изменений в списке), удаление элементов или реверсирование списка. Об этой функции легче рассуждать, основываясь только на ее сигнатуре.

Кроме… в Java всегда можно обмануть систему типов. Поскольку реализация этого универсального метода может использовать такие вещи, как instanceofпроверки и / или приведение к произвольным типам, наши рассуждения, основанные на сигнатуре типа, могут быть легко признаны бесполезными. Функция может проверять тип элементов и выполнять любое количество действий в зависимости от результата. Если эти взломы среды выполнения разрешены, сигнатуры параметризованных методов станут для нас гораздо менее полезными.

Если бы в Java не было стирания типов (то есть аргументы типа были переопределены во время выполнения), тогда это просто позволило бы больше затруднить рассуждение махинаций такого рода. В приведенном выше примере реализация может нарушить ожидания, установленные сигнатурой типа, только если в списке есть хотя бы один элемент; но если он Tбыл реифициро- ван, он мог это сделать, даже если список был пуст. Реифицированные типы просто увеличат (и без того очень многие) возможности препятствовать нашему пониманию кода.

Стирание шрифта делает язык менее «мощным». Но некоторые формы «власти» на самом деле вредны.

Лахлан
источник
1
Похоже, вы либо утверждаете, что дженерики слишком сложны для Java-разработчиков, чтобы их «понять и рассуждать», либо что им нельзя доверять, чтобы они не прострелили себе ногу, если представится такая возможность. Последнее, похоже, синхронизировано с идеей Java (например, без неподписанных типов и т. Д.), Но оно кажется разработчикам немного снисходительным
Basic
1
@Basic Я, должно быть, очень плохо выразился, потому что я совсем не это имел в виду. Проблема не в том, что универсальные шаблоны сложны, а в том, что такие вещи, как приведение типов, instanceofпрепятствуют нашей способности рассуждать о том, что делает код на основе типов. Если бы Java расширила аргументы типа, это только усугубило бы эту проблему. Стирание типов во время выполнения делает систему типов более полезной.
Lachlan
@lachlan это имело смысл для меня, и я не думаю, что нормальное прочтение этого могло бы вызвать оскорбление для разработчиков Java.
Роб Грант
3

Это не прямой ответ (ОП спросил «каковы преимущества», я отвечаю «каковы недостатки»)

По сравнению с системой типов C # стирание типов Java - настоящая боль для двоих.

Вы не можете реализовать интерфейс дважды

В C # можно реализовать как IEnumerable<T1>и IEnumerable<T2>безопасно, особенно если эти два типа не имеют общего предка (т.е. их предком является Object ).

Практический пример: в Spring Framework вы не можете реализовать ApplicationListener<? extends ApplicationEvent>несколько раз. Если вам нужно другое поведение на основе того, что Tвам нужно проверитьinstanceof

Вы не можете сделать новый T ()

(и для этого вам нужна ссылка на Class)

Как отмечали другие, выполнение эквивалента new T()можно выполнить только через отражение, только путем вызова экземпляра Class<T>, убедившись в параметрах, требуемых конструктором. C # позволяет делать это new T() только в том случае, если вы ограничиваетесь Tконструктором без параметров. Если Tне соблюдает это ограничение, возникает ошибка компиляции .

В Java вам часто придется писать методы, которые выглядят следующим образом

public <T> T create(....params, Class<T> classOfT)
{

    ... whatever you do
    ... you will end up
    T = classOfT.newInstance();


    ... or more advanced reflection
    Constructor<T> parameterizedConstructorThatYouKnowAbout = classOfT.getConstructor(...,...);
}

Недостатки в приведенном выше коде:

  • Class.newInstance работает только с конструктором без параметров. Если ничего не доступно, ReflectiveOperationExceptionвыдается во время выполнения
  • Отраженный конструктор не выделяет проблемы во время компиляции, как указано выше. Если вы проведете рефакторинг и обмениваете аргументы, вы узнаете только во время выполнения

Если бы я был автором C #, я бы представил возможность указывать одно или несколько ограничений конструктора, которые легко проверить во время компиляции (поэтому мне может потребоваться, например, конструктор с string,stringпараметрами). Но последнее - предположение

usr-local-ΛΩΝ
источник
2

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

public class GenericClass<T>
{
     private Class<T> targetClass;
     public GenericClass(Class<T> targetClass)
     {
          this.targetClass = targetClass;
     }

Затем этот класс может делать все, что было бы достижимо по умолчанию, если бы Java не использовала стирание: он может выделять новые Ts (при условии, что у Tнего есть конструктор, соответствующий шаблону, который он ожидает использовать) или массивы Ts, он может динамически проверять во время выполнения, является ли конкретный объект a, Tи изменять поведение в зависимости от этого и т. д.

Например:

     public T newT () { 
         try {
             return targetClass.newInstance(); 
         } catch(/* I forget which exceptions can be thrown here */) { ... }
     }

     private T value;
     /** @throws ClassCastException if object is not a T */
     public void setValueFromObject (Object object) {
         value = targetClass.cast(object);
     }
}
Жюль
источник
Кто-нибудь хочет объяснить причину отрицательного голоса? Насколько я понимаю, это прекрасное объяснение того, почему предполагаемый недостаток системы стирания типов в Java на самом деле вообще не является настоящим ограничением. Так в чем проблема?
Жюль
Я голосую за. Не то чтобы я поддерживаю проверку типов во время выполнения, но этот трюк может пригодиться в соляных шахтах.
techtangents
3
Java - это язык соляных шахт, что делает его очевидным, учитывая отсутствие информации о типах среды выполнения. Будем надеяться, что стирание типов будет отменено в будущей версии Java, что сделает обобщения гораздо более полезной функцией. В настоящее время общее мнение таково, что их соотношение тяги к весу ниже 1.
Марко Топольник,
Ну, конечно ... Пока ваш TargetType сам по себе не использует дженерики. Но это кажется довольно ограничивающим.
Basic
Он также работает с универсальными типами. Единственное ограничение заключается в том, что если вы хотите создать экземпляр, вам понадобится конструктор с правильными типами аргументов, но любой другой язык, о котором я знаю, имеет такое же ограничение, поэтому я не вижу ограничения.
Жюль
0

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

некромант
источник
3
Однако большая часть этих виртуальных диспетчеризации преобразуется в статические во время выполнения, так что это не так уж плохо, как может показаться.
Петр Колачковский
1
Совершенно верно, доступность компилятора во время выполнения позволяет java делать много интересных вещей (и достигать высокой производительности) по сравнению с c ++.
necromancer
вау, позвольте мне записать это где-нибудь, где java обеспечивает высокую производительность по сравнению с cpp ..
jungle_mole 01
1
@jungle_mole да, спасибо. мало кто осознает преимущество наличия компилятора, доступного во время выполнения, для повторной компиляции кода после профилирования. (или что сборка мусора - это большая ох (не мусор), а не наивное и неправильное убеждение, что сборка мусора - это большая ох (мусор), (или что, хотя сборка мусора представляет собой небольшое усилие, оно компенсируется предоставлением нулевой стоимости распределение)). Это печальный мир, в котором люди слепо принимают то, во что верит их сообщество, и перестают исходить из основных принципов.
некромант
0

Большинство ответов больше касаются философии программирования, чем реальных технических деталей.

И хотя этому вопросу более 5 лет, вопрос все еще остается: почему стирание шрифтов желательно с технической точки зрения? В конце концов, ответ довольно прост (на более высоком уровне): https://en.wikipedia.org/wiki/Type_erasure

Шаблоны C ++ не существуют во время выполнения. Компилятор генерирует полностью оптимизированную версию для каждого вызова, что означает, что выполнение не зависит от информации о типе. Но как JIT работает с разными версиями одной и той же функции? Не лучше ли иметь одну функцию? Не хотелось бы, чтобы JIT приходилось оптимизировать все его версии. Ну, а как насчет безопасности типов? Угадайте, что надо выйти из окна.

Но подождите секунду: как это делает .NET? Отражение! Таким образом, им нужно оптимизировать только одну функцию, а также получить информацию о типе времени выполнения. Вот почему дженерики .NET раньше работали медленнее (хотя стали намного лучше). Я не утверждаю, что это неудобно! Но это дорого, и его не следует использовать, когда это не является абсолютно необходимым (это не считается дорогостоящим в динамически типизированных языках, потому что компилятор / интерпретатор в любом случае полагается на отражение).

Таким образом, универсальное программирование со стиранием типа близко к нулевым накладным расходам (все еще требуются некоторые проверки / преобразования во время выполнения): https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Марвин Х.
источник