В этом ответе Я попытался создать статический метод утилиты, чтобы сделать List в Map:
public static <K, T> Map<K, T> toMapBy(List<T> list,
        Function<? super T, ? extends K> mapper) {
    return list.stream().collect(Collectors.toMap(mapper, Function.identity()));
}
Это прекрасно работает. Однако я обнаружил, что метод не может использоваться во всех тех же контекстах, что и выражение list.stream().collect(...). Метод не такой гибкий.
List<Student> students = Arrays.asList();
Map<Long, Student> studentsById1 = students.stream()
        .collect(Collectors.toMap(Student::getId, Function.identity()));
Map<Long, Student> studentsById2 = toMapBy(students, Student::getId);
Map<Long, Person> peopleById1 = students.stream()
        .collect(Collectors.toMap(Student::getId, Function.identity()));
Map<Long, Person> peopleById2 = toMapBy(students, Student::getId);  // compile error!
В этом примере Student является подтипом Person и имеет метод getId, который возвращает Long.
Последний оператор не работает с incompatible types: inference variable T has incompatible bounds ... (JDK 1.8.0_25). Есть ли способ определить параметры типа, чтобы статический метод работал в тех же контекстах, что и содержащееся выражение?
