Прогнозируемая общая специализация в 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>
не может быть подтипом rawBox
, (И по той же причине,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
будет использоваться.