Приведение к общей переменной

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

ArrayList<?> cislo = (ArrayList<? extends Number>) new ArrayList<Integer>();

Это может быть не лучший пример, но, надеюсь, вы получите мое мнение. Как это работает? К какому типу его получить?

Ответ 1

Бросок не нужен.

Назначения

ArrayList<?> list = new ArrayList<Integer>();
ArrayList<? extends Number> list2 = new ArrayList<Integer>();

не требует явного литья.

Подстановочные типы - это "более общие" / "менее конкретные" типы, чем конкретные типы, и верхние ограниченные типы подстановок (например, ? extends Number) более специфичны, чем неограниченные (?).

Более подробную информацию о связи между типами подстановочных знаков можно найти в Учебник Oracle по подстановочным символам и подтипированию.

Соответствующая часть JLS, определяющая это, 4.10.2. Подтипирование между классами и типами интерфейсов

Учитывая объявление общего типа C (n > 0), прямые супертипы параметризованного типа C, где Ti (1 ≤ я ≤ n) являются типом, являются следующими:

  • D, где D - общий тип, являющийся прямым супертипом общего типа C, а θ - подстановка [F1: = T1,..., Fn: = Tn].
  • C, где Si содержит Ti (1 ≤ я ≤ n) (п. 4.5.1).

[...]

который относится к 4.5.1. Тип Аргументы параметризованных типов

Говорят, что аргумент типа T1 содержит другой аргумент типа T2, написанный T2 <= T1, если набор типов, обозначаемый T2, является предположительно подмножеством множества типов, обозначаемых T1 при рефлексивном и транзитивном замыкании следующие правила (где <: обозначает подтипирование (§4.10)):

  • ? расширяет T < =? расширяет S, если T <: S

  • ? расширяет T < =?

[...]

Таким образом, это определение ArrayList<? extends Number> является супертипом ArrayList<Integer>, а ArrayList<?> является супертипом любого ArrayList<>, за исключением исходного типа.

Назначение переменной типа, являющегося супертипом, не требует кастинга.


Приведение требуется, если вы хотите присвоить что-то другого типа:

Object list = new ArrayList<Integer>();
//...somewhere else - the compiler does not know that list is always an ArrayList, but we tell it that we know what we are doing
List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning, but works

После этого акта вы можете, например, получить элементы из списка и рассматривать их как Number s. При выполнении команды cast при выполнении задания list, которое не является подтипом List<? extends Number>

    Object list = new HashSet<Integer>();
    List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning

не выполняется при запуске

java.lang.ClassCastException: java.util.HashSet cannot be cast to java.util.List


Проблемы возникают, когда общий тип не соответствует:

List<String> stringList = new ArrayList<String>();
stringList.add("hi");
Object list = stringList;
List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning, but (sadly) works

Number n = numbers.get(0); //fails: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

Это происходит потому, что во время выполнения стирается стирание типов списка. См. Также учебник по стиранию