Ограниченные подстановочные знаки Java Generics

Это довольно надуманный пример, который я делаю для изучения потоков Java, но, как мне кажется, застрял в общей проблеме с подстановочными знаками. Мой код ниже. Я пытаюсь прочитать каждый объект Employee из списка, применить функцию, получить идентификатор и снова установить его в новом объекте сотрудника. Однако код не будет компилироваться прямо в методе key.apply(emp). Сообщение об ошибке не особенно полезно для отладки, так как оно просто говорит: «Требуемый тип: захват ? extends Предоставляемый номер: захват ? расширяет номер. Требуемый и предоставленный выглядят одинаково. Что мне не хватает?

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;

public class Employee {
    private int id;
    private String name;
    Map<Function<Employee,? extends Number>, BiConsumer<Employee, ? extends Number>> map = new HashMap<>();

    public Employee(String name, int id) {
        this.name = name;
        this.id = id;
    }

    public Employee() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Function<Employee, Integer> func = Employee::getId;

    public BiConsumer<Employee, Integer> c = Employee::setId;

    public static void main(String[] args) {
        Employee e = new Employee();
        e.m1();
    }

    private void m1() {
        List<Employee> l = new ArrayList<>();
        l.add(new Employee("A", 100));
        l.add(new Employee("B", 100));
        l.add(new Employee("A", 101));
        l.add(new Employee("D", 102));
        l.add(new Employee("E", 103));
        l.add(new Employee("F", 104));
        l.add(new Employee("F", 102));
        Employee e = new Employee();
        map.put(func, c);
        map.forEach((key, value) -> l.stream()
                .forEach(emp -> value.accept(e, key.apply(emp)))); 
// key.apply(emp) shows a squigly line as an error.
//The error is,
//Required type: capture of ? extends Number 
//Provided: capture of ? extends Number. 
    }
}

person JavaNovice    schedule 13.06.2020    source источник


Ответы (1)


Честно говоря, я не понимаю основной идеи, но могу предложить следующие строки, чтобы сделать код компилируемым. Но проблема с этим: get-put-principle

Принцип получения и помещения: используйте подстановочный знак расширения, когда вы получаете значения только из структуры, используйте супер подстановочный знак, когда вы только помещаете значения в структуру, и не используйте подстановочный знак, когда вы получаете и помещаете оба значения.

В соответствии с этим:

List<? super Number> list1 = new ArrayList<>();
list1.add(new Integer(1));  // works
list1.add(new Double(2.2));

List<Number> list2 = new ArrayList<>();
list2.add(new Integer(1));  // works
list2.add(new Double(2.2));

List<? extends Number> list3 = new ArrayList<>();
list3.add(new Integer(1));  // doesn't work
list3.add(new Double(2.2)); // doesn't work

Вы можете изменить свой код следующим образом:

public class Employee {
    private int id;
    private String name;

    public Employee(String name, int id) {
        this.name = name;
        this.id = id;
    }

    public Employee() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Function<Employee, Integer> func = Employee::getId;
    public BiConsumer<Employee, ? super Number> c = (employee, number) -> employee.setId(number.intValue());
    Map<Function<Employee, ? extends Number>, BiConsumer<Employee, ? super Number>> map = new HashMap<>();

    public static void main(String[] args) {
        Employee e = new Employee();
        e.m1();
    }

    private void m1() {
        List<Employee> l = new ArrayList<>();
        l.add(new Employee("A", 100));
        l.add(new Employee("B", 100));
        l.add(new Employee("A", 101));
        l.add(new Employee("D", 102));
        l.add(new Employee("E", 103));
        l.add(new Employee("F", 104));
        l.add(new Employee("F", 102));
        Employee e = new Employee();
        map.put(func, c);
        map.forEach((key, value) -> l.stream()
                .forEach(emp -> value.accept(e, key.apply(emp))));
    }
}

Or

public class Employee {
    private int id;
    private String name;

    public Employee(String name, int id) {
        this.name = name;
        this.id = id;
    }

    public Employee() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Function<Employee, Integer> func = Employee::getId;
    public BiConsumer<Employee, Number> c = (employee, number) -> employee.setId(number.intValue());
    Map<Function<Employee, Integer>, BiConsumer<Employee, Number>> map = new HashMap<>();

    public static void main(String[] args) {
        Employee e = new Employee();
        e.m1();
    }

    private void m1() {
        List<Employee> l = new ArrayList<>();
        l.add(new Employee("A", 100));
        l.add(new Employee("B", 100));
        l.add(new Employee("A", 101));
        l.add(new Employee("D", 102));
        l.add(new Employee("E", 103));
        l.add(new Employee("F", 104));
        l.add(new Employee("F", 102));
        Employee e = new Employee();
        map.put(func, c);
        map.forEach((key, value) -> l.stream()
                .forEach(emp -> value.accept(e, key.apply(emp))));
    }
}
person rostIvan    schedule 13.06.2020