Как отличается дисперсия сайта использования Java от дисперсии сайта декларации C #? - PullRequest
34 голосов
/ 20 ноября 2010

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

Каковы плюсы и минусы каждого варианта?

Ответы [ 4 ]

38 голосов
/ 30 марта 2011

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

Прежде всегоЕсли я правильно помню, дисперсия использования сайта строго более мощная, чем дисперсия объявления сайта (хотя и ценой краткости), или, по крайней мере, символы подстановки Java (которые на самом деле более мощные, чем дисперсия использования сайта).Эта увеличенная мощность особенно полезна для языков, в которых интенсивно используются конструкции с сохранением состояния, таких как C # и Java (но Scala намного меньше, особенно потому, что его стандартные списки являются неизменяемыми).Рассмотрим List<E> (или IList<E>).Так как у него есть методы как для добавления E, так и для получения E, он инвариантен относительно E, и поэтому дисперсию на сайте декларации нельзя использовать.Однако при использовании дисперсии сайта использования вы можете просто сказать List<+Number>, чтобы получить ковариантное подмножество List и List<-Number>, чтобы получить контравариантное подмножество List.На языке объявления сайта разработчик библиотеки должен был бы создать отдельные интерфейсы (или классы, если вы разрешаете множественное наследование классов) для каждого подмножества, и List расширяет эти интерфейсы.Если разработчик библиотеки не делает этого (обратите внимание, что IEnumerable в C # выполняет только небольшое подмножество ковариантной части IList), то вам не повезло, и вам придется прибегнуть к тем же хлопотам, которые нужно сделатьна языке без каких-либо различий.

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

Конечно, эти две языковые возможности могут сосуществовать.Для параметров типа, которые естественно ковариантны или контравариантны (например, в IEnumerable / Iterator), объявите это в объявлении.Для параметров типа, которые естественно инвариантны (например, в (I)List), объявляйте, какую дисперсию вы хотите использовать каждый раз, когда вы ее используете.Просто не указывайте дисперсию сайта использования для аргументов с декларацией сайта, так как это только сбивает с толку.

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

16 голосов
/ 27 ноября 2010

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

Но имейте в виду, что ни Java, ни C # не являются примерами хорошего языкового дизайна.

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

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

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

Давайте рассмотрим Scala :Scala - это полностью объектно-ориентированный, функциональный гибрид, работающий на JVM.Они используют стирание типов, как Java, но как реализация Generics, так и (объявление-сайт) дисперсии легче понять, более прямолинейны и мощны, чем Java (или C #), потому что виртуальная машина не навязывает правила о том, как дисперсия должнаРабота.Компилятор Scala проверяет нотации отклонений и может отклонять необработанный исходный код во время компиляции вместо генерирования исключений во время выполнения, в то время как полученные файлы .class могут беспрепятственно использоваться из Java.

Один недостатокиз-за дисперсии сайта объявлений в некоторых случаях затрудняется вывод типов.

В то же время Scala может использовать примитивные типы, не упаковывая их в коллекции, как в C #, используя аннотацию @specialized, которая сообщаетКомпилятор Scala для генерации одной или нескольких дополнительных реализаций класса или метода, специализированного для запрошенного типа примитива.

Scala также может «почти» преобразовывать дженерики с помощью манифестов, что позволяет им извлекать родовые типы во время выполнения, как вC #.

1 голос
/ 27 ноября 2010

Недостатки обобщений стиля Java

Одним из следствий этого является то, что версия Java работает только со ссылочными типами (или коробочными типами значений), а не с типами значений.IMO - это самый большой недостаток, так как он предотвращает высокопроизводительные обобщения во многих сценариях и требует ручного написания специализированных типов.

Он не гарантирует инвариант типа «Этот список содержит только объекты типа x» и требуетпроверки во время выполнения на каждом получателе.Универсальный тип действительно существует.

При использовании отражения вы не можете спросить экземпляр универсального объекта, какие у него универсальные параметры.

Преимущества универсальных шаблонов стиля Java

Вы можете получить дисперсию / разыгрыш между различными общими параметрами.

0 голосов
/ 10 сентября 2013

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

C #: использовать дженерики для сайтов с версии C # 2.0. Добавлена ​​декларация сайта дисперсии в C # 4.0. Разбитые ковариантные массивы с другим синтаксисом, начиная с 1.0 (проблема, аналогичная Java). «reified» generics означает, что информация о типе не теряется во время компиляции.

Scala: Обе версии используют сайт / объявление-сайт с ранних версий языка (по крайней мере, с 2008 года). Массивы - это не отдельная языковая функция, поэтому вы используете один и тот же обобщенный синтаксис и правила дисперсии типов. Некоторые коллекции реализованы на уровне виртуальных машин с массивами JVM, поэтому вы получаете равную или лучшую производительность во время выполнения по сравнению с кодом Java.

Чтобы разобраться в проблеме безопасности типов массивов C # / Java: вы можете привести Dog [] к Pet [], добавить Cat и вызвать ошибку времени выполнения, которая не обнаруживается во время компиляции. Scala реализовал это правильно.

...