Передача Observable с указанным общим типом списка в качестве аргумента метода

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

class BaseClass { }
class SubClass extends BaseClass { }

static void listMethod(List<? extends BaseClass> list) { }

Вызов этого метода прост и работает так, как ожидалось:

    List<BaseClass> b = Collections.empty();
    List<SubClass> s = Collections.empty();

    listMethod(b);  // ok
    listMethod(s);  // ok

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

static void observableListMethod(Observable<List<? extends BaseClass>> listObservable) { }

К сожалению, попытки вызвать этот новый метод приводят к ошибкам несоответствия аргументов вида:

Observable<List<BaseClass>> cannot be converted to Observable<List<? extends BaseClass>>

В отличие от общей проблемы с универсальными коллекциями в Java проблема здесь, по-видимому, не в неправильном приведении допущений между коллекциями подклассов и базовых классов. Скорее, это больше связано с конкретным типом используемого Observable.

    Observable<List<BaseClass>> b = Observable.just(Collections.emptyList());
    Observable<List<SubClass>> s = Observable.just(Collections.emptyList());

    observableListMethod(b);    // argument mismatch / incompatible types
    observableListMethod(s);    // argument mismatch / incompatible types

    // I can map the List to a read-only variant...
    observableListMethod(b.map(Collections::unmodifiableList)); // ok (for readonly)
    observableListMethod(s.map(Collections::unmodifiableList)); // ok (for readonly)

Лучшее решение, которое я придумал до сих пор, — это изменить сигнатуру метода, чтобы указать конкретно BaseClass (не ? extends BaseClass), а затем выполнить некрасивую распаковку и преобразование List с помощью Rx:

static void observableListMethod2(Observable<List<BaseClass>> listObservable) { }
public static void callObservableListMethod2() {
    Observable<List<BaseClass>> b = Observable.just(Collections.emptyList());
    Observable<List<SubClass>> s = Observable.just(Collections.emptyList());

    observableListMethod2(b);    // ok
    observableListMethod2(s.flatMapIterable(it -> it).cast(BaseClass.class).toList()); // ok, but yuck!
}

Конечно, есть более чистый способ выполнить то, что я пытаюсь сделать?


person Myk Willis    schedule 10.04.2018    source источник
comment
Используйте 1_   -  person 4castle    schedule 10.04.2018
comment
@4castle Я не верю, что это дублирующий вопрос по отношению к упомянутому, по крайней мере, не очевидным образом. Я понимаю основные ловушки, часто возникающие при использовании универсальных коллекций, моя проблема здесь заключается в том, чтобы понять, почему общее решение для работы с типизированными коллекциями не распространяется непосредственно на случай Observable.   -  person Myk Willis    schedule 10.04.2018
comment
Я удалил свой закрытый голос, но оставлю ссылку для справки.   -  person 4castle    schedule 10.04.2018


Ответы (2)


Как показывает другой ответ, вы можете решить эту проблему, используя переменную типа. Но объяснить, что происходит;

static void observableListMethod(Observable<List<? extends BaseClass>> listObservable) { }

Требуется Observable<List<? extends BaseClass>> в качестве параметра. Другими словами, требуется Observable<T>, где T равно List<? extends BaseClass>>.

Теперь вы пытаетесь передать, например, Observable<List<BaseClass>>, то есть Observable<U>, где U равно List<BaseClass>, другому типу, чем T. Но поскольку дженерики инвариантны, вы не можете присвоить Observable<U> Observable<T>. Даже если U можно присвоить T.

Чтобы заставить его работать, вам понадобится Observable<? extends T> в качестве параметра. Если вы снова подставите T на List<? extends BaseClass>>, вы получите:

Observable<? extends List<? extends BaseClass>>

Если вы сделаете этот тип параметра, это должно скомпилироваться:

Observable<List<BaseClass>> b = Observable.just(Collections.emptyList());
Observable<List<SubClass>> s = Observable.just(Collections.emptyList());

observableListMethod(b);
observableListMethod(s); 
person Jorn Vernee    schedule 10.04.2018

Кажется, это может быть обходным путем:

static <T extends BaseClass> void observableListMethod(Observable<List<T>> listObservable)

Может быть, я неправильно понимаю вопрос, но, по крайней мере, он компилируется для меня.

person user3237736    schedule 10.04.2018
comment
да я тоже.. хD - person user3237736; 10.04.2018
comment
Почему это не сработает? В случае b T выводится как BaseClass, а в случае s выводится как SubClass. - person Jorn Vernee; 10.04.2018