import java.util.*;
import java.util.function.Function;

class LegacyWildcards {
    // legacy code
    private static void foo(Function<String, Number> func) {
        // ...
    }

    // legacy code
    private static void foo(List<Number> list) {
        // ...
    }

    // legacy code
    private static void foo(Comparator<Integer> comparator) {
        // ...
    }

    // этот метод принимает на вход функцию с:
    //  - слишком широкой областью задания
    //  и
    //  - слишком конкретным множеством значений
    @SuppressWarnings("unchecked")
    private static <T, R> Function<T, R> vary(Function<? super T, ? extends R> f0) {
        // здесь нам на руку работает type erasure:
        // f0 превратится в объект raw типа Function,
        // который кастить можно уже к чему угодно только лишь ценой warning'а
        // (а warning можно подавить)
        return (Function<T, R>) f0;
    }

    // кастит список вверх
    @SuppressWarnings("unchecked")
    private static <T> List<T> covary(List<? extends T> list) {
        return (List<T>) list;
    }

    // кастит компаратор вниз
    @SuppressWarnings("unchecked")
    private static <T> Comparator<T> contravary(Comparator<? super T> comparator) {
        return (Comparator<T>) comparator;
    }

    public static void main(String[] args) {
        Function<CharSequence, Integer> func = seq -> 1;
        // ошибка, потому что генерики инвариантны
        // foo(func);

        // сработает, потому что будут же просто передаваться
        // только строки, с которыми func значет, как справляться,
        // потому что она может справляться с любыми CharSequence
        //
        // но это выглядит не очень
        foo((Function<String, Number>)(Function<?, ?>) func);

        // так тоже сработает, потому что компилятор сам
        // подставит правильные типы в лямбда-выражении, но
        // это выглядит немного обскурно
        foo(func::apply);

        // тоже работает и немного лучше читается, потому что
        // производится явный каст
        // а ещё там есть некоторая защита, благодаря которой
        // нельзя будет сделать небезопасный каст
        foo(vary(func));

        // то же самое можно делать с массивами, если
        // тип, заданный в объявлении метода имеет слишком общий
        // типовый параметр
        List<Integer> intList = new ArrayList<>();
        foo(covary(intList));

        Comparator<Number> comparator = (n1, n2) -> 0;
        foo(contravary(comparator));
    }
}