0


Can someone help me with understanding logic behind this code:

import java.util.List;
import java.util.Arrays;

interface DoubleValue<T> {
    public void dv(T e);
}

public class InTest2 {

    public static void main(String[] a) {
        DoubleValue<Number> func = e -> System.out.println(e.doubleValue());
        List<Number> l = Arrays.asList(1, 2, 3);
        InTest2.sumOfListNotWorking(l, func);
        InTest2.sumOfListWorking(l, func);
        InTest2.sumOfListWorkingSuper(l, func);
    }

    // I thought this should work
    public static void sumOfListNotWorking(List<? extends Number> list, DoubleValue<? extends Number> f) {
        for (Number n : list)
            f.dv(n);  // This line give error: The method dv(capture#2-of ? extends Number) in the type DoubleValue<capture#2-of ? extends Number> is not applicable for the arguments (Number)
    }

    public static void sumOfListWorking(List<? extends Number> list, DoubleValue<Number> f) {
        for (Number n : list)
            f.dv(n);
    }

    public static void sumOfListWorkingSuper(List<? extends Number> list, DoubleValue<? super Number> f) {
        for (Number n : list)
            f.dv(n);
    }
}

sumOfListNotWorking:
Here I pass to DoubleValue.dv(<? extends Number> e) a Number and I thought it's OK. But compiler says that it's not. Why?
I expect with DoubleValue<Number>.dv as e -> e.doubleValue() this should be typical consumer: I get value <? extends Number> and do something with it.


sumOfListWorkingSuper:
Behavior of sumOfListWorkingSuper puzzles me too. Interface DoubleValue<? super Number> can't be a consumer cause value of <? super Number> can't be changed in place (Number is immutable), but compiler is OK with it.

UPDATE:
Apparently I didn't understand correctly logic of "producer" and "consumer".

sumOfListNotWorking:
This method doesn't compile because I use:

  • List<? extends Number> list as a producer (in method I take out values from list with loop).
  • DoubleValue<? extends Number> as a consumer (in method I pass Number to f.dv(n)), but when I try to pass value Number to f compiler told me that f is defined as a producer (<? extends Number> - have some values of Number or it's children) and it can not accept any values. What if I passed to f argument DoubleValue<Integer> then I would try to put Number where should be Integer - this isn't possible. All this error happens because I define a producer instead of a consumer.

Thanks for pointing out word accept in logic of a consumer and a producer.

Maxim Andreev
  • 182
  • 2
  • 10
  • You'll get plenty of information here https://stackoverflow.com/questions/19795709/understanding-upper-and-lower-bounds-on-in-java-generics – Mikhail Kholodkov Jun 06 '18 at 12:51
  • Possible duplicate of [Understanding upper and lower bounds on ? in Java Generics](https://stackoverflow.com/questions/19795709/understanding-upper-and-lower-bounds-on-in-java-generics) – Mikhail Kholodkov Jun 06 '18 at 12:52

2 Answers2

2

List<? extends Number> means "we don't know what this List contains, but whatever it is, it is a descendant of Number or Number itself."

Similarly, DoubleValue<? extends Number> means "we don't know what this DoubleValue can accept to operate with, but whatever it is it accepts, it is a descendant of Number or Number itself".

Here the relevant part is "we don't know what it accepts". We don't know, so you can't give anything to it and expect it will be accepted: because you don't know if it is accepted. The rest of the information is irrelevant. It's relevant in a List<? extends Number> because there at least we know that whatever elements it gives out, can be safely cast to Number after they're given out. But your DoubleValue class doesn't give out anything. It only takes in things. So all that "but we know that it is a descendant of" is completely useless.

<? extends Stuff> is useful on classes that give out objects. It's useless on those that don't give out objects.

<? super Stuff> is useful on classes that take in objects. It's useless on those that don't take in objects.

kumesana
  • 2,495
  • 1
  • 9
  • 10
  • I'd like to suggest extending you answer by showing how to fix this in code (making the method generic and adding the "unknown" type as parameter, say, `T`). – lexicore Jun 06 '18 at 12:58
  • @lexicore thank you for the suggestion, but it is my impression that there is nothing to fix here. OP has code that works, and only asked why extends Number> doesn't work. I hope I explained clearly enough why it doesn't work and OP should stick to the code that does. – kumesana Jun 06 '18 at 13:01
  • Please see my answer. The OP may not have explicitly asked for it, but I think it's a good additional info. If you'd be willing to take this into your answer, I'll remove mine. – lexicore Jun 06 '18 at 13:10
1

In addition to @kumesana's answer, this is how you can fix your method:

public static <T extends Number> void sumOfListNotWorking(
    List<? extends T> list,
    DoubleValue<? super T> f) {

    for (T n : list)
        f.dv(n);

}

You only needed to make that "unknown" type in wildcards known (T).

Also see the PECS rule. Here, list provides items (i.e. it is a producer), f accepts items (it is a consumer). So type in list should extend T, type in f should super T. This allows passing List<Integer> and DoubleValue<Number>.

lexicore
  • 42,748
  • 17
  • 132
  • 221