Прогнозируемая общая специализация в Java 9 или новее, vs List <int>: как будет работать .remove()?

Общая специализация наряду со значениями типов является проецируемой функцией будущих JVM; ссылку на страницу проекта Valhalla здесь.

Теперь, из того, что я понимаю, тогда стало бы возможно объявить:

final List<int> myList = new ArrayList<>(); // for instance

Но тогда List определяет другой метод .remove() в дополнение к тому, который определен в интерфейсе Collection, который принимает int в качестве аргумента, который является индексом в удаляемом списке; поэтому в настоящее время содержание List в приведенном ниже примере:

final List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.remove(2);

будет [1, 2], а не [1, 3] (выбирается наиболее конкретная перегрузка).

Однако, если в будущем мы сможем объявить List<int>, у нас будет проблема: какая перегрузка метода remove будет выбрана?

Ответы

Ответ 1

Этот ответ основан на этой статье Брайана Гетца от декабря 2014 года. Это последнее, что я мог найти по этому вопросу; обратите внимание, однако, что этот документ является "неофициальным эскизом", так что пока ничего не найдено относительно вашего вопроса.

Во-первых, List<int> не будет подтипом List<Integer> (Subtyping):

Изначально также может показаться разумным, что Box<int> может быть подтипом raw Box. Но, учитывая нашу стратегию перевода, класс Box не может быть суперклассом любого класса, представляющего Box<int>, так как тогда Box<int> будет иметь поле t типа Object, тогда как t должно быть типа int. Таким образом, Box<int> не может быть подтипом raw Box. (И по той же причине Box<int> не может быть подтипом Box<Integer>.)

...

Так как generics инвариантны, неудивительно, что List<int> не является подтипом List<Integer>. Немного удивительно, что специализированный тип не может взаимодействовать с его сырой копией. Однако это не является необоснованным ограничением; не только сырые типы не поощряются (были введены исключительно для поддержки постепенной миграции из не общего кода в общий код), но все же можно писать полностью общий код с использованием общих методов - см. "Общие методы".

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

Некоторые перегрузки, которые действительны сегодня, станут проблематичными по специализации. Например, эти методы будут иметь проблему, если они специализированы с помощью T=int:

public void remove(int position);
public void remove(T element);

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

Предлагаемое решение называется методом "пилинга" :

Рассмотрим пару перегрузок в классе List-like:

interface ListLike<T> {
    public void remove(int position);
    public void remove(T element);
}

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

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

Если бы мы писали этот класс с нуля в мире после специализации, мы могли бы написать его как:

interface ListLike<any T> {
    void removeByIndex(int position);
    void removeByValue(T element);
}

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

interface ListLike<any T> {
    // New methods added to the generic layer
    void removeByValue(T element);
    void removeByIndex(int pos);

    layer<ref T> {
        // Abstract methods that exist only in the ref layer
        void remove(int pos);
        void remove(T element);

        // Default implementations of the new generic methods
        default void removeByIndex(int pos) { remove(pos); }
        default void removeByValue(T t) { remove(t); }
    }
}

Теперь эталонные экземпляры имеют remove(T) и remove(int) (а также новые методы removeByIndex и removeByValue), обеспечивающие совместимость, а специализации имеют непроблемные перегрузки removeByValue(T) и removeByIndex(int). Существующие реализации ListLike будут продолжать компилироваться, поскольку новые методы имеют стандартную реализацию для ссылочных специальностей (которые просто соединяются с существующими методами удаления). Для экземпляров значений removeByIndex и removeByValue рассматриваются как абстрактные и должны быть предоставлены, но удаление вообще не существует.

Этот метод также позволяет использовать метод "реализации по частям"; можно объявить абстрактный метод в общем слое и предоставить конкретные реализации как на уровне значений, так и на уровне ссылок. Если бы мы разрешили слои для T=int, это также позволило бы использовать технику "специализации специализации".

С помощью этого метода будет сохранена обратная совместимость и будут использоваться новые методы removeByValue и removeByIndex.