Почему некоторые утверждают, что реализация Java дженериков плоха?

Самый простой способ - поместить это в AppDelegate. Для этого вы можете использовать функцию application(_:didFinishLaunchingWithOptions:).

Если вам нужно настроить многое, вы можете создать для этого отдельный класс «Координатор», а не загромождать AppDelegate.

Использование с UIMainStoryboardFile в Info.plist

Если в вашем Info.plist установлена ​​клавиша UIMainStoryboardFile, основная раскадровка будет автоматически загружена AppDelegate. Затем он инициализирует контроллер начального вида перед вызовом application(_:didFinishLaunchingWithOptions:).

Решением для этого является удаление клавиши UIMainStoryboardFile и загрузка раскадровки вручную следующим образом:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  // Do your setup here

  let storyboard = UIStoryboard(name: "Main", bundle: nil)
  guard let rootViewController = storyboard.instantiateInitialViewController() else {
    fatalError("No intitial view controller configured in Main.storyboard")
  }

  window.rootViewController = rootViewController
  window.makeKeyAndVisible()
  return true
}
111
задан Community 23 May 2017 в 12:25
поделиться

13 ответов

Плохо:

  • информация о Типе потеряна во время компиляции, таким образом, во время выполнения Вы не можете сказать то, что вводит, она "предназначена", чтобы быть
  • , не Может использоваться для типов значения (это - важная персона - в.NET List<byte>, действительно поддерживается byte[], например, и никакая упаковка не требуется)
  • , Синтаксис для вызова общих методов сосет (IMO)
  • , Синтаксис для ограничений может получить путание
  • , Wildcarding обычно путает
  • Различные ограничения из-за вышеупомянутого - бросающий и т.д.

Хороший:

  • Wildcarding позволяет ковариантности/контравариантности быть указанной при вызове стороны, которая очень аккуратна во многих ситуациях
  • , Это лучше чем ничего!
141
ответ дан Jon Skeet 24 November 2019 в 03:00
поделиться

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

26
ответ дан Community 24 November 2019 в 03:00
поделиться
  1. реализация Во время выполнения (т.е. не стирание типа);
  2. способность использовать типы примитивов (это связано с (1));
  3. , В то время как wildcarding полезен синтаксис и знающий, когда использовать его, что-то, что озадачивает много людей. и
  4. Никакое повышение производительности (из-за (1); дженерики Java являются синтаксическим сахаром для Объектов castingi).

(1) приводит к некоторому очень странному поведению. Лучший пример, о котором я могу думать. Примите:

public class MyClass<T> {
  T getStuff() { ... }
  List<String> getOtherStuff() { ... }
}

затем объявляют две переменные:

MyClass<T> m1 = ...
MyClass m2 = ...

Теперь вызов getOtherStuff():

List<String> list1 = m1.getOtherStuff(); 
List<String> list2 = m2.getOtherStuff(); 

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

я также упомяну свое любимое объявление от JDK:

public class Enum<T extends Enum<T>>

Кроме wildcarding (который является ассортиментом) я просто думаю, что дженерики .NET лучше.

14
ответ дан cletus 24 November 2019 в 03:00
поделиться

Основная проблема состоит в том, что Java на самом деле не имеет дженериков во времени выполнения. Это - функция времени компиляции.

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

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

Замечательный обзор различий здесь: http://www.jprl.com/Blog/archive/development/2007/Aug-31.html

17
ответ дан JaredPar 24 November 2019 в 03:00
поделиться

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


   public class MyClass {
     public T getStuff() {
       return new T();
     }
    }

- jeffk ++

8
ответ дан jdkoftinoff 24 November 2019 в 03:00
поделиться

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

Map someMap;

Теперь, я должен объявить это как

Map<String, List<String>> someMap;

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

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

И что Вы действительно покупаете для всего этого дополнительного многословия? Сколько раз у Вас действительно были проблемы, куда кто-то поместил Целое число в набор, это, как предполагается, содержит Строки, или где кто-то пытался вытащить Строку из набора Целых чисел? За мои 10 лет опыта, работая при создании коммерческих JAVA-приложений, это только что никогда не было большим источником ошибок. Так, я не действительно уверен, что Вы получаете для дополнительного многословия. Это действительно просто кажется мне дополнительным бюрократическим багажом.

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

List someList = someMap.get("some key");

, я должен сделать

List someList = (List) someMap.get("some key");

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

я думаю, что Java был бы путем, более обеспеченным, если 1) это не добавило дженериков, но 2) вместо этого позволил неявный кастинг от супертипа до подтипа. Позвольте неправильным броскам быть пойманными во времени выполнения. Затем у меня, возможно, была простота определения

Map someMap;

и более позднее выполнение

List someList = someMap.get("some key");

, весь хлам закончится, и я действительно не думаю, что ввел бы большой новый источник ошибок в мой код.

9
ответ дан John Topley 24 November 2019 в 03:00
поделиться

Дженерики Java проверяются на правильность во время компиляции, и затем вся информация о типе удалена (процесс называют стирание типа . Таким образом, универсальный List<Integer> будет уменьшен до необработанный тип , неуниверсальный List, который может содержать объекты произвольного класса.

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

ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if(li.getClass() == lf.getClass()) // evaluates to true
  System.out.println("Equal");
6
ответ дан Anton Gogolev 24 November 2019 в 03:00
поделиться

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

существуют некоторые, кто также чувствует, что реализация Java дженериков увеличила сложность языка к недопустимому уровню (см. Ken Arnold" Дженерики, Продуманные Вредный "). Angelika Langer часто задаваемые вопросы Дженериков дают довольно хорошую идею относительно того, как сложные вещи могут стать.

4
ответ дан Zach Scrivena 24 November 2019 в 03:00
поделиться

Java не осуществляет Дженериков во время выполнения, только во время компиляции.

Это означает, что можно сделать интересные вещи как добавление неправильных типов к универсальным наборам.

3
ответ дан Powerlord 24 November 2019 в 03:00
поделиться

Мне жаль, что это не была Wiki, таким образом, я мог добавить к другим людям..., но...

проблемы:

  • Стирание Типа (никакая доступность во время выполнения)
  • Никакая поддержка примитивных типов
  • Incompatability с Аннотациями (они были оба добавлены в 1,5, я все еще не уверен, почему аннотации не позволяют дженериков кроме стремительного движения функций)
  • Incompatability с Массивами. (Иногда я действительно хочу сделать что-то как Класс <? расширяет MyObject> [], но мне не разрешают)
  • подстановочный синтаксис Wierd и поведение
  • то, что универсальная поддержка непоследовательна через классы Java. Они добавили его к большинству методов наборов, но время от времени, Вы сталкиваетесь с экземпляром где не там.
4
ответ дан Laplie Anderson 24 November 2019 в 03:00
поделиться

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

3
ответ дан Szymon Rozga 24 November 2019 в 03:00
поделиться

Игнорирование всего беспорядка при стирании типов, указанные обобщения просто не работают.

Это компилирует:

List<Integer> x = Collections.emptyList();

Но это синтаксическая ошибка:

foo(Collections.emptyList());

Где foo определяется как :

void foo(List<Integer> x) { /* method body not important */ }

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

5
ответ дан 24 November 2019 в 03:00
поделиться

Если вы послушаете Java Posse # 279 - Интервью с Джо Дарси и Алексом Бакли , они говорят об этой проблеме. Здесь также есть ссылка на сообщение в блоге Нила Гафтера под названием Reified Generics for Java , в котором говорится:

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

Это сообщение в блоге ссылается на более старую запись, Puzzling Through Erasure: answer section , в которой подчеркивается пункт о совместимости миграции в требованиях.

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

2
ответ дан 24 November 2019 в 03:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: