Jump to content

Подстановочный знак (Java)

В языке программирования Java знак подстановочный ? это особый вид аргумента типа [1] который контролирует безопасность типов при использовании универсальных (параметризованных) типов. [2] Его можно использовать в объявлениях переменных и созданиях экземпляров, а также в определениях методов, но не в определении универсального типа. [3] [4] Это форма от места использования аннотации отклонения , в отличие от аннотаций отклонения от места определения, встречающихся в C# и Scala .

Ковариация для универсальных типов

[ редактировать ]

В отличие от массивов (которые являются ковариантными в Java [2] ), разные экземпляры универсального типа несовместимы друг с другом даже явно. [2] Например, декларации Generic<Supertype> superGeneric; Generic<Subtype> subGeneric; заставит компилятор сообщать об ошибках преобразования для обоих типов приведения (Generic<Subtype>)superGeneric и (Generic<Supertype>)subGeneric.

Эту несовместимость можно смягчить с помощью подстановочного знака, если ? используется как фактический параметр типа. [2] Generic<?> является супертипом всех параметризаций универсального типа Generic. Это позволяет объектам типа Generic<Supertype> и Generic<Subtype> быть безопасно присвоено переменной или параметру метода типа Generic<?>. [2] С использованием Generic<? extends Supertype> позволяет то же самое, ограничивая совместимость Supertype и его дети. [5] Другая возможность заключается в Generic<? super Subtype>, который также принимает оба объекта и ограничивает совместимость Subtype и все его родители. [5]

Подстановочный знак как тип параметра

[ редактировать ]

В теле универсального модуля параметр (формального) типа обрабатывается как его верхняя граница (выраженная с помощью extends; Object если нет ограничений). [5] Если возвращаемый тип метода является параметром типа, результат (например, типа ?) может ссылаться на переменную типа верхней границы (или Object). В другом направлении подстановочный знак не подходит ни для одного другого типа, даже для Object: Если ? был применен как формальный параметр типа метода, ему нельзя передать никакие фактические параметры. Однако объекты неизвестного типа могут быть прочитаны из универсального объекта и присвоены переменной супертипа верхней границы.

Пример кода для Generic<T extends UpperBound> сорт:

class Generic <T extends UpperBound> {    private T t;    void write(T t) {        this.t = t;    }    T read() {        return t;    }}

Пример кода, использующий Generic<T extends UpperBound> сорт:

...final Generic<UpperBound> concreteTypeReference = new Generic<UpperBound>();final Generic<?> wildcardReference = concreteTypeReference;final UpperBound ub = wildcardReference.read(); // Object would also be OKwildcardReference.write(new Object()); // type errorwildcardReference.write(new UpperBound()); // type errorconcreteTypeReference.write(new UpperBound()); // OK...

Ограниченные подстановочные знаки

[ редактировать ]

Ограниченный подстановочный знак — это знак с верхним или нижним ограничением наследования . Границей подстановочного знака может быть тип класса, тип интерфейса , тип массива или переменная типа. Верхние границы выражаются с помощью ключевого слова « extensions» , а нижние границы — с помощью ключевого слова «super» . Подстановочные знаки могут указывать либо верхнюю , либо нижнюю границу, но не обе.

Верхние границы

[ редактировать ]

Верхняя граница подстановочного знака должна быть подтипом верхней границы соответствующего параметра типа, объявленного в соответствующем универсальном типе. [5] Пример подстановочного знака, который явно указывает верхнюю границу:

Generic<? extends SubtypeOfUpperBound> referenceConstrainedFromAbove;

Эта ссылка может содержать любую параметризацию Generic аргумент типа которого является подтипом SubtypeOfUpperBound. Подстановочный знак, в котором явно не указана верхняя граница, фактически аналогичен знаку, имеющему ограничение extends Object, поскольку все ссылочные типы в Java являются подтипами Object.

Нижние границы

[ редактировать ]

Подстановочный знак с нижней границей, например

Generic<? super SubtypeOfUpperBound> referenceConstrainedFromBelow;

может содержать любую параметризацию Generic любой аргумент типа которого является одновременно подтипом верхней границы соответствующего параметра типа и супертипом SubtypeOfUpperBound. [5]

Создание объекта с подстановочным знаком

[ редактировать ]

Никакие объекты не могут быть созданы с аргументом типа подстановочного знака: например, new Generic<?>() запрещено. На практике в этом нет необходимости, потому что если бы кто-то захотел создать объект, который можно было бы присвоить переменной типа Generic<?>, можно просто использовать любой произвольный тип (который подпадает под ограничения подстановочного знака, если таковой имеется) в качестве аргумента типа.

Однако, new ArrayList<Generic<?>>() разрешено, поскольку подстановочный знак не является параметром экземпляра типа ArrayList. То же самое справедливо и для new ArrayList<List<?>>().

В выражении создания массива тип компонента массива должен быть доступным для повторного использования, как определено Спецификацией языка Java, раздел 4.7. Это означает, что если тип компонента массива имеет какие-либо аргументы типа, все они должны быть неограниченными подстановочными знаками (подстановочные знаки, состоящие только из ?) . Например, new Generic<?>[20] правильно, в то время как new Generic<SomeType>[20] нет.

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

Пример: списки

[ редактировать ]

В Java Collections Framework класс List<MyClass> представляет собой упорядоченную коллекцию объектов типа MyClass.Верхние границы задаются с помощью extendsList<? extends MyClass> представляет собой список объектов некоторого подкласса MyClass, т. е. любой объект в списке гарантированно принадлежит к типу MyClass, поэтому его можно перебирать, используя переменную типа MyClass[6]

public void doSomething(List<? extends MyClass> list) {    for (final MyClass object : list) { // OK        // do something    }}

Однако не гарантируется, что можно добавить любой объект типа MyClass в этот список:

public void doSomething(List<? extends MyClass> list) {    final MyClass m = new MyClass();    list.add(m); // Compile error}

Обратное верно для нижних границ, которые задаются с помощью superList<? super MyClass> представляет собой список объектов некоторого суперкласса MyClass, т.е. список гарантированно сможет содержать любой объект типа MyClass, поэтому можно добавить любой объект типа MyClass:

public void doSomething(List<? super MyClass> list) {    final MyClass m = new MyClass();    list.add(m); // OK}

Однако не гарантируется, что можно будет перебирать этот список, используя переменную типа MyClass:

public void doSomething(List<? super MyClass> list) {    for (final MyClass object : list) { // Compile error        // do something    }}

Чтобы иметь возможность делать и то, и другое, добавьте объекты типа MyClass в список и перебрать его, используя переменную типа MyClass, а List<MyClass> необходим, и это единственный тип List это и то и другое List<? extends MyClass> и List<? super MyClass>. [7]

Мнемоника PECS (Producer Extends, Consumer Super) из книги Эффективная Java» « Джошуа Блоха дает простой способ запомнить, когда использовать подстановочные знаки (соответствующие ковариации и контравариантности) в Java. [5]

См. также

[ редактировать ]
  1. ^ «Глава 4. Типы, значения и переменные» . docs.oracle.com . Проверено 3 ноября 2020 г.
  2. ^ Перейти обратно: а б с д и Bloch 2018 , стр. 117–122, глава §5, пункт 26. Не используйте необработанные типы.
  3. ^ Гилад Брача (июнь 2004 г.), «4. Подстановочные знаки», Generics in the Java Programming Language (PDF) , получено 6 марта 2016 г.
  4. ^ «8.1.2 Универсальные классы и параметры типа», Спецификация языка Java , Oracle , получено 6 марта 2016 г.
  5. ^ Перейти обратно: а б с д и ж Bloch 2018 , стр. 139–145, глава §5, пункт 31. Используйте ограниченные подстановочные знаки для повышения гибкости API.
  6. ^ Наследование (объектно-ориентированное программирование)
  7. ^ Синтаксис Java (обобщенные)
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: c14015afe0e251dcc5263b8e4bb130e9__1687392840
URL1:https://arc.ask3.ru/arc/aa/c1/e9/c14015afe0e251dcc5263b8e4bb130e9.html
Заголовок, (Title) документа по адресу, URL1:
Wildcard (Java) - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)