FULL PRODUCT VERSION :
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux 4.4.0-66-generic #87-Ubuntu SMP Fri Mar 3 15:29:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
The following code does not compile:
import java.util.Optional;
class B<T> {}
public class Test {
static B<Integer> fn(B<Object> a) { return null; }
public static void main(String[] args) {
Optional<B> opt = Optional.empty(); // note raw type parameter B
B<Integer> res = opt.map(Test::fn).get(); // COMPILE ERROR: incompatible types: Object cannot be converted to B<Integer>
}
}
It looks like javac infers the result of opt.map(Test::fn) to be Optional<Object> instead of Optional<B<Integer>>. I expected it to be inferred as Optional<B<Integer>> because fn returns B<Integer>.
Also, it's curious that the program does compile if we explicitly cast the result of opt.map as follows:
B<Integer> res = ((Optional<B<Integer>>) opt.map(Test::fn)).get();
It also compiles successfully if we make fn take a raw B, instead of B<Object>.
If the signature of fn is erased because of unchecked conversion, shouldn't the erasure return a raw B? Does the compiler erase the return to Object, or why does the compilation fail?
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the program shown below
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected program to compile successfully
ACTUAL -
Compilation error
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Compilation error: incompatible types: Object cannot be converted to B<Integer>
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.Optional;
class B<T> {}
public class Test {
static B<Integer> fn(B<Object> a) { return null; }
public static void main(String[] args) {
Optional<B> opt = Optional.empty(); // note raw type parameter B
B<Integer> res = opt.map(Test::fn).get(); // COMPILE ERROR: incompatible types: Object cannot be converted to B<Integer>
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Multiple workarounds:
0) Don't use raw types in the first place
1) declare fn() as: static B<Integer> fn(B a) { return null; } // NOTE: parameter is raw B instead of B<Object>
2) explicit cast: B<Integer> res = ((Optional<B<Integer>>) opt.map(Test::fn)).get();