1

I've discovered a problem compiling a class that happens only in maven, not inside Eclipse.

The code is the following:

@Test
public void compliationFailOnMaven() {

    Optional<List<String>> list = getDummyList();

    List<Integer> hascodes = list.orElse(Collections.EMPTY_LIST).stream().map(value -> value.hashCode()).collect(toList());

    assertThat(hascodes).isNotNull();
}

private Optional<List<String>> getDummyList() {

    return Optional.ofNullable(new ArrayList<String>(0));
}

If you insert this code into a maven project and try to execute it using mvn clean test it fails due a compilation problem:

[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] /java-tests/general-test/src/test/java/com/java8/stream/StreamTest.java:[113,125] incompatible types: java.lang.Object cannot be converted to java.util.List<java.lang.Integer>
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.844 s
[INFO] Finished at: 2015-11-19T18:07:36+01:00
[INFO] Final Memory: 26M/277M
[INFO] ------------------------------------------------------------------------

However, if you import this project into eclipse, it can compile and execute it without any problem.

The problem is the use of generics. If I replace this line:

List<Integer> hascodes = list.orElse(Collections.EMPTY_LIST).stream().map(value -> value.hashCode()).collect(toList());

with this one:

List<Integer> hascodes = list.orElse(new ArrayList<String>(0)).stream().map(value -> value.hashCode()).collect(toList());

Everything works in both environments: eclipse and maven.

Does anybody know why it's happening this?

Why are they producing different results?

I only have one JVM installed, so both are using the same Java version

jfcorugedo
  • 9,793
  • 8
  • 39
  • 47
  • 5
    http://stackoverflow.com/questions/14870819/what-is-the-difference-between-collections-emptylist-and-collections-empty-lis – Tunaki Nov 19 '15 at 17:17
  • Eclipse has a small bug here, I don't think this should compile. – Tunaki Nov 19 '15 at 17:22
  • @Tunaki My intuition here is that Eclipse actually has the correct (or my expected) behavior, which is an unchecked conversion from `List` to `List` when passing `EMPTY_LIST` to `orElse`. But it doesn't really matter, because the problem here is that OP shouldn't be trying to use `EMPTY_LIST` in the first place. – Radiodef Nov 19 '15 at 17:40
  • @Radiodef: No, the error is correct. The return type of the raw signature of `collect` is `Object` not `List`. If it were `List`, you could perform an unchecked conversion to `List`, but it isn’t. – Holger Nov 19 '15 at 17:50
  • The question is: Why are they producing different results? I only have one JVM installed, so both are using the same Java version – jfcorugedo Nov 20 '15 at 10:35
  • @jfcorugedo JVM != compiler; there's one compiler in eclipse and one in the JDK, and the latter is used by maven – Erwin Bolwidt Nov 20 '15 at 10:42
  • Eclipse has its own compiler, so the fact that you only have one JVM installed doesn't matter. I guess that when you run it you won't see the difference because the code does the right thing. To be sure, try to do the already suggested unchecked conversion, then compile with eclipse and run it. – francesco foresti Nov 20 '15 at 10:43
  • You could get more information about this topic in this question.. http://stackoverflow.com/questions/5633424/is-it-an-eclipse-or-maven-compiler-plugin-bug-the-generics-class-cast-issue – awsome Nov 20 '15 at 10:45
  • Yes, you're right, JVM and compiler is not the same, but both of them are packaged inside JDK, and I have only one installed. I've configured eclipse to use my external JDK (inside "installed JRE" option) and maven are using the same JDK, so Why are they giving different results? – jfcorugedo Nov 20 '15 at 12:09
  • You have 2 compilers installed: _javac_, from the JDK, and the _Eclipse Compiler for Java_ (ECJ). You are most likely hitting a bug in ECJ like [Bug 333011](https://bugs.eclipse.org/bugs/show_bug.cgi?id=333011) mentioned in the question referred by @awsome – Didier L Nov 20 '15 at 14:12
  • @Holger, I see no reason why type checking should use the raw signature of `collect`. If you still think so, please explain (see also my answer). – Stephan Herrmann Nov 29 '15 at 22:45

1 Answers1

1

The fundamental reason why different behavior can be observed in some situations is: Eclipse uses its own compiler "ECJ", which has been explained before. Maven, by contrast, normally delegates to javac - although it can be told to use ECJ, too, if you want to be sure maven builds show the same result as what you see in Eclipse.

Of course both compilers should obey to JLS and thus show the same behavior, but in reality due to the complexity of JLS any real world compiler has bugs, resulting in deviations between compilers.


For this given difference in particular, I don't see why javac should come to different results depending on the argument to orElse(). Note the signature:

public final class Optional<T> {
    ....
    public T orElse(T other)
    ....
}

Note, that only the class is generic, not the method. Since T is List<String> (from the declaration of list), orElse also returns a List<String>, no type inference involved at this stage. Since we know that the tail should compile fine once we have a List<String>, there's no reason any compiler should report an error.

The only problem is in passing the raw List as an argument but that's fully handled by the unchecked warning at this location.

I'm surprised to see javac rejecting a program due to problems with raw types, because most trouble in this area results from https://bugs.openjdk.java.net/browse/JDK-8026527 which however speaks of admitting programs that shouldn't be.

Edit: For completeness: I assumed the following imports:

import java.util.*;
import static java.util.stream.Collectors.toList;

We need a definition of toList to be fully sure what happens.

Community
  • 1
  • 1
Stephan Herrmann
  • 7,963
  • 2
  • 27
  • 38
  • Yes, I agree with you. I don't know why the compiler gives different behaviour. I guess maven and eclipse are using different compilers or different options in the compiler. That's the reason of my question. – jfcorugedo Nov 28 '15 at 23:11