0

This is a rather contrived example that I'm using to learn Java streams but I'm stuck with a generic wild card issue, I believe. My code is below. I'm trying to read every Employee object from a list, apply a function to get the id and set it in a new employee object over again. However, the code won't compile right in the key.apply(emp) method. The error message isn't particularly useful to debug as it simply says, Required type: capture of ? extends Number Provided: capture of ? extends Number. The required and provided looks to be the same. What am I missing?

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. 
    }
}
rgettman
  • 176,041
  • 30
  • 275
  • 357
JavaNovice
  • 1,083
  • 1
  • 12
  • 20

1 Answers1

0

The problem with that one is: get-put-principle

The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use super wildcard when you only put values into a structure, and don't use a wildcard when you both get and put.

According to that:

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

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

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

The same story with your example:

map.forEach((key, value) -> l.forEach(emp -> {
    final Number id = key.apply(emp);
    value.accept(e, id);  // compiler error
}));

With the wildcard like ? extends Number the only value can be used here is null

BiConsumer<Employee, ? extends Number> biConsumer = (emp, num) -> {};
biConsumer.accept(new Employee(), null); // works
rostIvan
  • 264
  • 1
  • 8