2

I don't understand why the following program fails to compile:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Main {
    public static void main(String[] args) {
        System.out.println(new ArrayList<Integer>(Arrays.asList(1, 2)).stream().map((Integer s) -> {
                return "" + (s * s);
        }).collect(Collectors.toCollection(ArrayList<String>::new)));
    }
}

whereas the following one compiles cleanly, the only difference being that I extracted the argument of println into a variable (whose type is even left to infer to the compiler).

import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Main {
    public static void main(String[] args) {
        var r = new ArrayList<Integer>(Arrays.asList(1, 2)).stream().map((Integer s) -> {
                return "" + (s * s);
        }).collect(Collectors.toCollection(ArrayList<String>::new));
        System.out.println(r);
    }
}

For the first one, javac (16.0.1) reports:

$ javac -Xdiags:verbose  Main.java
Main.java:8: error: method println in class PrintStream cannot be applied to given types;
        System.out.println(new ArrayList<Integer>(Arrays.asList(1, 2)).stream().map((Integer s) -> {
                          ^
  required: String
  found:    ArrayList<String>
  reason: argument mismatch; inference variable R has incompatible bounds
      lower bounds: String,Collection<T#2>,Object
      lower bounds: ArrayList<String>
  where R,A,T#1,T#2,C are type-variables:
    R extends Object declared in method <R,A>collect(Collector<? super T#1,A,R>)
    A extends Object declared in method <R,A>collect(Collector<? super T#1,A,R>)
    T#1 extends Object declared in interface Stream
    T#2 extends Object declared in method <T#2,C>toCollection(Supplier<C>)
    C extends Collection<T#2> declared in method <T#2,C>toCollection(Supplier<C>)
1 error

Since I'm not a Java programmer, I can't tell if I'm facing a compiler bug, or a true limitation of the Java typing system that I don't understand. In the latter case, please point me to the appropriate documentation.

Thanks in advance.

Lino
  • 19,604
  • 6
  • 47
  • 65
akim
  • 8,255
  • 3
  • 44
  • 60
  • 1
    @Lino thanks for the suggestion. But this piece of code is just one instance of a more general pattern, in generated code. I'm interested in truly understanding the problem, not just working around. – akim Jul 07 '21 at 08:09
  • Well, are you using Eclipse? – MC Emperor Jul 07 '21 at 08:09
  • The error ceases to appear when either using: `Collectors.>toCollection(ArrayList::new)` or `Collectors.toList()` – Lino Jul 07 '21 at 08:09
  • @ernest_k it fails with Java 8 too. (at least on my machine) – Lino Jul 07 '21 at 08:10
  • Well, the type system is fairly complex, and if you let the compiler deduce (too) many types, then the compiler *may* have a hard type making it work. – MC Emperor Jul 07 '21 at 08:12
  • 1
    I tested the first piece of code with a JUnit and a main using java8, it works in both cases for me. The version I'm using is: `java1.8.0_201`. – Simone Lungarella Jul 07 '21 at 08:14
  • Java 12.0.1 [doesn't work](https://ideone.com/regLWX) either. – MC Emperor Jul 07 '21 at 08:23
  • 1
    Simplified example: `System.out.println(Stream.of("a", "b").collect(Collectors.toCollection(ArrayList::new)))` – MC Emperor Jul 07 '21 at 08:33
  • 2
    Are you deliberately trying to make this code as unreadable as possible? This `new ArrayList(Arrays.asList(1, 2)).stream()` contains an entirely unnecessary copying from a `List` to an `ArrayList`, you could just use `Arrays.asList(1, 2).stream()` or the even shorter `Stream.of(1, 2)`. Then, why are you using the statement syntax `(Integer s) -> { return "" + (s * s); }` instead of the simpler expression syntax `(Integer s) -> "" + (s * s)`? That’s all distracting from what the code actually does. – Holger Jul 07 '21 at 08:41
  • I think this behaviour is described in the [Java Language Specification § 15.12](https://docs.oracle.com/javase/specs/jls/se16/html/jls-15.html#jls-15.12). I can't extactly tell which part describes this specific problem, since the document is, well, pretty technical. – MC Emperor Jul 07 '21 at 08:52
  • 2
    Strongly related: [Ambiguity error while trying to print result of JAVA8 Collector](https://stackoverflow.com/q/54801795/2711488). It seems the Eclipse team did their homework and fixed the bug(s) on their side, whereas team `javac` is still sleeping, [bug JDK-8054721](https://bugs.openjdk.java.net/browse/JDK-8054721) is still open and doesn’t even show plans to address the issue. – Holger Jul 07 '21 at 08:55
  • Replace the method reference `ArrayList::new` with `() -> new ArrayList<>()` and it works. – MC Emperor Jul 07 '21 at 08:55
  • @Holger Thanks for the suggestion, but as I already specified in an earlier comment this is generated code, and the point is trying to understand what is happening here in the general case, not how a human would write this particular piece of code. – akim Jul 07 '21 at 08:56
  • 2
    @akim I wouldn’t trust a generator that produces such code. – Holger Jul 07 '21 at 08:58
  • @Holger Great catch! Thanks a lot for the references ([Ambiguity error while trying to print result of JAVA8 Collector](https://stackoverflow.com/q/54801795/2711488) and [JDK-8054721](https://bugs.openjdk.java.net/browse/JDK-8054721)), it does appear to be that very issue. – akim Jul 07 '21 at 08:59

0 Answers0