Java type error when compiling code with javac 8, which worked fine with javac 6

I have this class that simplifies some code that I found in a project that migrates from Java 6 to Java 8:

public class Unification { final class Box<A> {} final class MyMap<A, B extends Box<? extends A>> {} MyMap<?, ?> getMap() { return new MyMap<Object, Box<Object>>(); } <A, B extends Box<? extends A>> void setMap(final MyMap<A, B> m) {} void compileError() { setMap(getMap()); } } 

This is a very small example, just to demonstrate the problem, the actual code makes a little more sense. The question seems to be a fairly general, although, therefore, abstract example. The main problem is this: for some reason, javac does not want to accept an expression of type MyMap<?, ?> As an argument to the setMap() method, although this should, in my opinion, be well-typed.

The code compiles without errors using javac 6, but I get this obscure error message when I use javac 8:

 C:\System9\KWS_sparse\sourcesNG\Domain\src\uz\Unification.java (21:9) error: method setMap in class Unification cannot be applied to given types; required: Unification.MyMap<A,B> found: Unification.MyMap<CAP#1,CAP#2> reason: inference variable A has incompatible bounds equality constraints: CAP#1 lower bounds: Object where A,B are type-variables: A extends Object declared in method <A,B>setMap(Unification.MyMap<A,B>) B extends Unification.Box<? extends A> declared in method <A,B>setMap(Unification.MyMap<A,B>) where CAP#1,CAP#2 are fresh type-variables: CAP#1 extends Object from capture of ? CAP#2 extends Unification.Box<? extends CAP#1> from capture of ? 

The error message seems to indicate that for a parameter of the first type MyMap no unification was found, javac cannot find a type that combines with both CAP#1 , which represents the first substitution argument in the actual parameter setMap() and A , which is the corresponding parameter of the type of the formal parameter setMap() . Although it seems to me that A and CAP#1 should be completely unified, they will then be an existential type, introduced by erasing the actual types in the getMap() signature.

Can anyone determine what is going on here? Has javac 6 mistakenly accepted this code? Also, is there a not-too-intrusive (and javac 6 compatible) way to guide javac 8 toward proper unification?

EDIT: I tried to suggest introducing a variable from stackoverflow.com/questions/23063474/, but that doesn't seem to help, I get the same compilation error.

EDIT2: Clarified the "intent" of the sample code.

EDIT3: Renamed Map to MyMap , it seems too difficult to introduce a native Map type.

+5
source share
1 answer

The problem can be circumvented by introducing an intermediate method and thereby forcing javac to perform type inference in two steps. The first method accepts only one type parameter and leaves the second as a wildcard. Javac is able to correctly infer a parameter of the same type for this method. The second and original method can then correctly infer a parameter of the second type from the provided argument.

 public class Unification { final class Box<A> {} final class MyMap<A, B extends Box<? extends A>> {} MyMap<?, ?> getMap() { return new MyMap<Object, Box<Object>>(); } <A> void setMap(final MyMap<A, ?> m) { doSetMap(m); } <A, B extends Box<? extends A>> void doSetMap(final MyMap<A, B> m) {} void worksFineNow() { setMap(getMap()); } } 
0
source

Source: https://habr.com/ru/post/1258343/


All Articles