Предикаты и дженерики

243
06 сентября 2018, 14:30

Начал разбираться с предикатами и дженериками. И вот у меня есть вот такой вот статический метод:

public static <T> int findFirst(List<T> list, int begin, int end, Predicate<? extends T> p){
        for(int i = begin; i <= end; i++)
            if(p.test(list.get(i)))
                return i;
        return -1;
    }

Но среда разработки выдает ошибку

T cannot be converted to capture#1 of ? extends T

Не могу понять почему, ведь означает класс Т и его потомки, почему p.test() не принимает элемент листа

Answer 1

Предположим, у нас имеется иерархия классов:

class K { public byte k_value; }  // суперкласс для T
class T : K { public int t_value; }  // T унаследован от K
class Q : T { public long q_value; }  // Q унаследован от T

Когда мы пишем параметр Predicate<? extends T>, это означает, что можно в этот параметр передать методы со следующими сигнатурами:

bool ConditionT(T value);
bool ConditionQ(Q value);

т.е. параметр передаваемого метода должен иметь тип T или тип любого потомка T (в данном случае подходит Q).

Предположим, в ваш метод findFirst мы передали последний вариант: ConditionQ.

Что произойдёт в этом случае? А получится при вызове p.test() попытка передать объект типа T в том месте, где требуется тип Q. В результате получим ошибку приведения типов, что логично: в конкретно данном примере, у объекта Q можно ожидать наличие поля q_value, а объект T его не содержит - так что передавать нельзя.

Когда же пишем параметр Predicate<? super T>, это означает, что можно в этот параметр передать методы со следующими сигнатурами:

bool ConditionK(K value);
bool ConditionT(T value);

т.е. параметр передаваемого метода должен иметь тип T или тип любого предка T (в данном случае подходит K).

Предположим, в ваш метод findFirst мы передали первый вариант: ConditionK.

В этом случае, при вызове p.test() имеется попытка передать объект типа T в том месте, где требуется тип K. Попытка будет успешной, поскольку класс T является производным от K и гарантированно содержит в себе интерфес типа K. В конкретно данном примере можно говорить о том, что не только объект типа K содержит поле k_value, но и объект типа T тоже (в силу наследования).

Answer 2

Например, в качестве Т передается Person, а (? extends Person) будет Student, у которого есть поля, которых нет у Person, но в методе test() надо именно по этим полям задать логику проверки. Как быть?

READ ALSO
Начать писать код для Android

Начать писать код для Android

я новичок в программированииНе так давно начал изучать C# и Java

257
Поместить значения из datatable(jsf) в коллекцию

Поместить значения из datatable(jsf) в коллекцию

Имеется datatable на JSF странице с столбцами, которой являются название продукта, его цена и input поле количества этого товараПри нажатии кнопки...

278
Bootstrap стили только для DatePicker

Bootstrap стили только для DatePicker

Использую DatePicker от bootstrap, но вся стилизация bootstrap мне не нужна, нарушает собственнуюКак я могу выделить только нужные мне стили? Не могу понять,...

247
Блок с информацией занял всю ширину

Блок с информацией занял всю ширину

Подскажите пожалуйста, где ошибка в коде? Надо чтобы было как на картинке, но в браузере блок с ценой расположен под блоком с информацией

290