Загальна та стиральна система


10

Генеріки на Java реалізовані за допомогою стирання типу. JLS говорить, що натхнення було зворотною сумісністю. З іншого боку, дженерики C # підлягають повторенню.

Теоретично, які переваги та недоліки мають Generics як "стирання" чи "повторне"?

Ява відсутня на чомусь?

Відповіді:


8

Ну, мотивація (зворотна сумісність) є і перевагою, і недоліком. Це є недоліком, оскільки всі ми вважаємо за краще мати повторювані типи, але ціна, яку потрібно платити, була високою. Розглянемо вибір дизайну в C #. Вони мають повторювані типи, але тепер у них є копії API. Отже, уявіть Java API, де ми також мали дублікати API для кожного параметризованого класу. Тепер уявіть себе, як переносите тисячі рядків коду зі застарілих класів у нові загальні класи. Тепер хто б не вважав дублікати API недоліком? Але ей, вони мають повторювані типи!

Отже, головною мотивацією була "еволюція, а не революція". І логічно, що кожне рішення має компроміси.

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

Існування мостових методів (цей синтаксично сформований метод компілятора для збереження бінарної сумісності) також може розглядатися як недолік. І це може бути саме однією з причин помилок, про які я згадував у попередньому пункті.

Основні недоліки випливають із уже очевидного факту, що існує один клас, а не декілька класів для родових типів. В якості іншого прикладу розглянемо, що перевантаження методу з тим же загальним класом у Java не вдається:

public void doSomething(List<One>);
public void doSomething(List<Two>);

Щось можна вважати недоліком типів, що повторюються (принаймні в C #), - це те, що вони викликають вибух коду . Наприклад, List<int>це один клас, а List<double>інший зовсім інший, як це List<string>і є List<MyType>. Тому класи повинні визначатися під час виконання, спричиняючи вибух класів та витрачаючи цінні ресурси під час їх генерування.

Щодо того, що неможливо визначити асиміляцію на new T()Java, згадану в іншій відповіді, також цікаво врахувати, що це не лише питання стирання типу. Він також вимагає існування конструктора за замовчуванням, тому C # вимагає для цього "нового обмеження". (Дивіться, чому новий T () не можливий на Java , автор Алекс Баклі).


3
Я думаю, що я маю рацію, кажучи, що в CLR, для посилання типу T s, ви отримуєте лише одну копію Class<T>коду для всіх Ts; плюс додаткова копія для кожного використовуваного типу значення T .
АакашМ

@AakashM: Правильно, є один клас опорного типу на загальний, однак кожен тип значення створює свою власну версію. Це пов'язано з наданням спеціалізованого місця для зберігання на основі типу, всі посилання займають однакове сховище, але типи значень займають різні сховища для кожного типу.
Гуванте

6

Мінусом стирання типу є те, що ви не знаєте під час виконання типу загального типу. Це означає, що ви не можете застосувати до них роздуми і не можете їх створити під час виконання.

На Java не можна зробити щось подібне:

public class MyClass<T> {
    private T t;

    //more methods    
    public void myMethod() {
        //more code
        if (someCondition) {
            t = new T();//illegal
        } else {
            T[] array = new T[];//illegal
        }            
    }       
}

Для цього існує вирішення, але для цього потрібно більше коду. Перевага, як ви вже згадували, - зворотна сумісність.


3

Ще одна перевага стираних дженериків полягає в тому, що різні мови, що компілюються в JVM, використовують різні стратегії генерики, наприклад, сайт визначення Scala порівняно з коваріацією сайту використання Java. Крім того, більш високі типи дженериків Scala було б складніше підтримувати в рефікованих типах .Net, тому що фактично Scala on .Net ігнорував несумісний формат редагування C #. Якби ми реіфікували дженерики в JVM, швидше за все, ці модифіковані дженерики не були б придатними для функцій, які нам справді подобаються у Scala, і ми б застрягли в чомусь неоптимальному. Цитуючи з блогу Ола Біні ,

Це все означає, що якщо ви хочете додати в JVM рефіковані дженерики, ви повинні бути впевнені, що ця реалізація може охоплювати як всі статичні мови, які хочуть зробити інновації у власній версії дженериків, так і всі динамічні мови, які хочуть створити хорошу реалізацію та приємний інтерфейс із бібліотеками Java. Тому що якщо ви додасте доопрацьовані дженерики, які не відповідають цим критеріям, ви пом'якшите інновації та зробите це набагато складніше використовувати JVM як багатомовну віртуальну машину.

Особисто я не вважаю необхідність використання TypeTagконтексту, пов'язаного в Scala, для конфліктних перевантажених методів як недолік, оскільки це переміщує накладні та негнучкість переробки від глобальної (ціла програма та всі можливі мови) на сайт використання на кожній мові питання, яке трапляється лише рідше.



Ще одна причина переваги стираних дженериків полягає в тому, що залежності слід вводити таким чином, щоб це відповідало вирішенню проблеми виразів. Якщо ви тестуєте для конкретних випадків типів або жорсткого кодування фабрики у вашій загальній функції, то ви робите розширюваність неправильно.
Шелбі Мур III
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.