Верхний и нижний ограниченные подстановочные знаки в возвращаемом типе Java Универсальный метод - PullRequest
0 голосов
/ 26 октября 2018

Я пытался решить проблему, в которой я не могу понять часть ответа.

Ниже приводится класс BackLister:

public class BackLister {
    // INSERT HERE
    {
        List<T> output = new LinkedList<T>();
        for (T t : input)
            output.add(0, t);
        return output;
    }
}

Вопрос, который можетбыть вставленным в // INSERT HERE в BackLister классе для компиляции и запуска без ошибок?

Ниже приведены параметры:

A. public static <T> List<T> backwards(List<T> input)
B. public static <T> List<T> backwards(List<? extends T> input)
C. public static <T> List<T> backwards(List<? super T> input)
D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)
F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)

Я понимаю, что A и B верны, так какдля работы for (T t : input) элементы в input должны иметь тип T или подтип T.

Но я не могу понять, почему параметры D и E верны?

Я понимаю следующее:

  1. public static <T> List<? extends T> backwards(List<T> input) означает, чтотип возвращаемого значения должен быть List из T или подкласс T.
  2. public static <T> List<? super T> backwards(List<T> input) означает, что тип возвращаемого значения должен быть List из T или суперкласс T.

Может ли кто-нибудь помочь мне понять это?

Ответы [ 3 ]

0 голосов
/ 26 октября 2018

Ниже изображение описывает отношение подтипа в Generics:

List<T> is a subtype of List<? super T>
Also List<T> is a subtype of List<? extends T>

Вот почему опции D и E. верны.

enter image description here

Вы можете сослаться на страницу: https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html

0 голосов
/ 26 октября 2018

Опции D и E действительны, поскольку существуют существенные отношения супер-подтип среди универсальных типов, которые позволяют определить больший набор типов, которые метод может принимать или возвращать.

Следовательно, действует следующее (D):

public static <T> List<? extends T> backwards(List<T> input) {
    return List.of();
}

// there exist a super-subtype relationships among List<? extends Number> and List<Long>
List<? extends Number> list = backwards(List.<Long>of(1L, 2L));

, поскольку тип Long является членом семейства типов, которое обозначает подстановочный знак ? extends Number (семейство типов, являющихся подтипами Number и самого типа Number).

Следующий фрагмент кода также действителен (E):

public static <T> List<? super T> backwards(List<T> input) {
    return List.of();
}

List<? super Long> ints = backwards(List.<Long>of(1L, 2L));

потому что тип Long является членом семейства типов, которое обозначает подстановочный знак ? super Long (семейство типов, которые являются супертипами Long и самого типа Long).

Итак, ваше понимание верно.

Дополнительная литература .

0 голосов
/ 26 октября 2018

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

class Food {}
class Apple extends Food {}
class Orange extends Food {}
class RedApple extends Apple {}

List<Food> listFood = new ArrayList<>();
List<Apple> listApple = new ArrayList<>();
List<Orange> listOrange = new ArrayList<>();
List<RedApple> listRedApple = new ArrayList<>();

Теперь начните с первого:

A. public static <T> List<T> backwards(List<T> input)

Этот метод принимает только List<T> и возвращает List<T>, и вы не можете отправить listApple и вернуть listRedApple. (однако ваш список возврата может содержать RedApple, поскольку он расширяется Apple, но тип списка должен быть List<Apple> и ничего более)

B. public static <T> List<T> backwards(List<? extends T> input)

Вы можете отправить listRedApple и вернуть listApple, но вы знаете, что listRedApple - это "расширение Apple" , поэтому в теле метода java распознает T как Apple. Затем, если вы используете, можете добавлять элементы в listRedApple, которые отправляются в качестве аргумента, вы можете добавить Apple в listRedApple, что не соответствует действительности !!! поэтому компилятор избегает этого и выдает ошибку компиляции. В B вы можете только читать элементы (и получать его как T), но вы не можете ничего добавить к нему.

C. public static <T> List<T> backwards(List<? super T> input) 

Вы можете отправить listApple, а затем в тело метода вы можете добавить что-нибудь, что расширяет Apple, потому что компилятор видит T как Apple, а в списке все, что супер из T , вы можете добавить что-нибудь расширяет Apple.
Однако на этот раз вы ничего не можете прочитать, потому что вы не знаете его тип, за исключением того, что вы получаете его как Object. (Это список "? Super T" )

Как вы видите здесь, есть разница между ? супер и ? продлить . один из них дает вам доступ для записи, а другой - для чтения. Это реальное использование подстановочных знаков.

D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)   

Если вы отправите listApple, тогда вы вернете List<? extends Apple>, но вы можете присвоить его любому из listFood или listApple или listRedApple, потому что List<? extends Apple> может содержать Apple или RedApple или что-то еще и мы не можем присвоить его любому List<T>, потому что тогда мы можем добавить T к этому списку и, возможно, T и ? extends T не совпадают. это одинаково для D и E. Вы можете присвоить его List<? extends Apple> для 'E и List<? super Apple> для D и отправить их методу, который нуждается в них в качестве параметра.

F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)

Дайте ошибки компиляции, потому что подстановочный знак не может использоваться как это.

Надеюсь, это поможет вам.
Если что-то не так, любой комментарий приветствуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...