Прогнозируемая общая специализация в Java 9 или новее, против 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> ( Подтип):

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

...

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

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

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

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

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

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

Рассмотрим пару перегрузки в классе, подобном списку:

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 будет использоваться.

Другие вопросы по тегам