5

Sorry for the vague title. I have this piece of code which compiles on Eclipse Juno (4.2) but not javac (1.7.0_09):

package test;

public final class Test {
    public static class N<T extends N<T>> {}

    public static class R<T extends N<T>> {
        public T o;
    }

    public <T extends N<T>> void p(final T n) {}

    public void v(final R<?> r) {
        p(r.o);       // <-- javac fails on this line
    }
}

The error is:

Test.java:13: error: method p in class Test cannot be applied to given types;
        p(r.o);
        ^
  required: T
  found: N<CAP#1>
  reason: inferred type does not conform to declared bound(s)
    inferred: N<CAP#1>
    bound(s): N<N<CAP#1>>
  where T is a type-variable:
    T extends N<T> declared in method <T>p(T)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends N<CAP#1> from capture of ?
1 error

So the questions are:

  1. Is this a javac bug or Eclipse bug?

  2. Is there any way to make this compile on javac, without changing the signature of the v method (i.e. keep the wildcard)?

    I know changing it to <T extends N<T>> void v(final R<T> r) does make it compile, but I would like to know if there's way to avoid this first. Also, the method p cannot be changed to <T extends N<?>> void p(final T n) because the content have types which requires the exact constraint T extends N<T>.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • @Nambari: It is also 1.7. Also, Eclipse does not use `javac` to compile the code, but its own compiler. – kennytm Nov 03 '12 at 17:47
  • It compiles in my eclipse with java 6 what java version are you using? – Amit Deshpande Nov 03 '12 at 17:47
  • @AmitD: Yes it works on Eclipse on both 1.6 and 1.7, but it won't compile with OpenJDK 6 and 7's javac, nor [Sun's JDK 6 on ideone](http://ideone.com/Z03W7V). – kennytm Nov 03 '12 at 17:48
  • @Nambari: http://stackoverflow.com/questions/3061654/what-is-the-difference-between-javac-and-the-eclipse-compiler. – kennytm Nov 03 '12 at 17:49
  • I don't think there is a way to make this compile using javac unless you make the change as you suggested. – kosa Nov 03 '12 at 18:05

1 Answers1

6

Wildcards are limited in that they break recursive expressions like T extends X<T> that type parameters allow. We know what you're trying to do is safe based on the following:

  1. r.o is of type T (declared by R), which is or extends N<T>.
  2. The method p takes an argument of type T (declared by p), which also is or extends N<T>.
  3. So even though r is typed as R<?>, a call p(r.o) should theoretically be legal.

This is possibly the reasoning of the eclipse compiler (known to make correct allowances for certain nuances of generics where javac doesn't).

Assuming you want to compile with javac and can't change the signature of v like you mentioned, the best you can do is resort to using a raw type, which "opts out" of generic type checking:

public void v(final R<?> r) {
    //necessary to placate javac - this is okay because [insert above reasoning]
    @SuppressWarnings("rawtypes")
    N nRaw = r.o;
    p(nRaw);
}
Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181