Как добавить элементы в список <T extends I1 & I2>? - PullRequest
4 голосов
/ 08 июля 2019

У меня проблемы с эффективным использованием дженериков.Большую часть времени, когда я сталкиваюсь с проблемой, описанной ниже, я в конечном итоге нахожу другой способ выполнить то, что мне нужно, но у меня возникает ощущение, что мне не хватает понимания относительно использования обобщений в Java.

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

Пожалуйста, посмотрите код ниже, например (s):

public interface I1 {
}

public interface I2 {
}

public class ValidType implements I1, I2 {
}

public class OtherValidType extends ValidType {
}

public class Foo {
    private static <T extends I1 & I2> List<T> getList() {
        List<T> list = new ArrayList<>();
        list.add(new ValidType());
        list.add(new OtherValidType());
        return list;
    }
}

Компилятор даже не позволит мне сделать что-то более простое, например:

public class Foo {
    private static <T extends ValidType> List<T> getList() {
        List<T> list = new ArrayList<>();
        list.add(new ValidType());
        return list;
    }
}

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

Все вышеперечисленное приводит к ошибке компиляции "add (T) в List не можетбыть примененным к (ValidType) ", например.

Где я неправильно понимаю использование обобщенных типов в качестве возвращаемых типов / параметров типа?

Ответы [ 2 ]

4 голосов
/ 08 июля 2019

Откуда вы знаете, что T в обоих примерах является суперклассом ValidType или OtherValidType?

T может быть совершенно другим классом, который также может реализовывать интерфейсы I1 и I2, и в этом случае вы не можете поместить в него экземпляры ValidType и OtherValidType.

В качестве второго примера попробуйте:

List<OtherValidType> list = Foo.getList();

Должно быть ясно, что T - это OtherValidType, но вы все же пытаетесь вставить в него ValidType, но ValidType не является подклассом OtherValidType (это суперкласс).

Есть случаи, когда вы можете использовать конструкцию, например:

private static <T extends I1 & I2> List<T> getList(T a, T b) {
    List<T> list = new ArrayList<>();
    list.add(a);
    list.add(b);
    return list;
}

, но если вы не используетеВы не знаете, что это за тип T, вы никогда не узнаете, можно ли ему присвоить конкретные известные классы ValidType и OtherValidType.

0 голосов
/ 09 июля 2019

Для этого метода:

private static <T extends ValidType> List<T> getList() {
    List<T> list = new ArrayList<>();
    list.add(new ValidType());
    return list;
}

это как если бы вы объявили (если бы это было возможно):

List<T extends ValidaType> list = new ArrayList<>();

Единственное, что вы можете поместить в такой List, это T; хотя язык не позволяет эту декларацию. С другой стороны, язык позволяет объявлять что-то вроде этого, используя так называемый подстановочный знак (используя его здесь, чтобы доказать свою точку зрения):

List<? extends ValidType> list = new ArrayList<>();
ValidType vt = list.get(0);

Вы все еще не можете ничего положить (вы можете null, но это не главное), но когда вы извлекаете что-то из этого List - вы можете получить только ValidType и это принуждение вы получаете. Вы можете поместить в него любые подтипы (например, T), но вы можете получить только супертип, потому что вы на самом деле не знаете, какие субтипы были там вставлены. Таким образом, ваш метод:

private static <T extends ValidType> List<T> getList() {
    List<T> list = new ArrayList<>();
    return list;
} 

будет применяться при вызове:

List<OtherValidType> l1 = getList(); // compiles
List<String> l2 = getList(); // does not compile
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...