5
votes

I have discovered the following problems with generics. Consider the generic interface

public interface A<X> {
    X get();
    void doStuff(X x);
}

Now, let's assume the following method definition:

public <T extends A<?>> void foo(T t) {
    bar(t);
}

Due to the wildcard, the type information for the return type of get() is insufficient. Therefore, I have to delegate to another method that "binds" this wildcard to a fresh type variable:

private <X> void bar(A<X> t) {
    X x = t.get();
    t.doStuff(x);
}

The invocation of bar() in foo is not allowed, the compiler outputs the following error message:

The method bar(A) in the type test is not applicable for the arguments (T)

However, if I change the method foo() to

public <T extends A<?>> void foo(T t) {
    A<?> u = t; // No explicit cast required, no "unchecked" warning!
    bar(u);
}

it works. Why? Is this a compiler error? Any comments on this would be very much appreciated.

Notes:

  • The reason why I don't simply declare method foo as void foo(A) is that I'm actually using the upper type bound interesection (&).
  • The reason why I don't declare X as a type variable in foo() is that I actually have the problem at class level and don't want to unnecessarily increase the number of type parameters of this class.
2
are you using the -Xlint compiler flag? If not, try compiling with thatHunter McMillen
I agree with @kan. The java compiler does, in fact compile this. I used 1.6.0_27. Eclipse does give the exact error you gave, so maybe you're using it?Joseph Gordon
Created the eclipse bug: bugs.eclipse.org/bugs/show_bug.cgi?id=364200 using @kan's example.Joseph Gordon
Does not compile using javac 1.7.0.Joseph Gordon

2 Answers

2
votes

I've checked the code:

public class Test
{
    public interface A<X> {
        X get();
        void doStuff(X x);
    }
    public <T extends A<?>> void foo(T t) {
        bar(t);
    }

    private <X> void bar(A<X> t) {
        X x = t.get();
        t.doStuff(x);
    }
}

it works. javac 1.6.0_22. Where do you have the error? Or I using another code?

1
votes

The code:

public class Test
{
    public interface A<X> {
        X get();
        void doStuff(X x);
    }
    public <T extends A<?>> void foo(T t) {
        bar(t);
    }

    private <X> void bar(A<X> t) {
        X x = t.get();
        t.doStuff(x);
    }
}

compiles for only select versions of Java 1.6. This bug was logged for eclipse, but was rejected with the following explanation by Srikanth Sankaran:

I believe the eclipse compiler behavior is correct and is matched by JDK5 and JDK7 (latest). It would appear the JDK6 behavior is a regression which has since been fixed.

Basically, here is a brief explanation of what is going on here:

Given the generic method bar and the call site as given by bar(t), the inference algorithm has no constrains to work with to infer the type of the type variable X. So after considering the arguments and expected return type etc, X is still unresolved and per specification is resolved to be the published lower bound - i.e X is inferred to be Object and the method becomes void bar(A) t); Since the actual parameters cannot be converted to formal parameters, the inferred method has to be rejected leaving us with no applicable candidates. Hence the call must be rejected.

The reason the call bar( ( A )t ); succeeds is that in this case X is inferred to be "capture#1-of ?" and the generic method parameterized with this substitution becomes void bar(A) for which the parameter compatibility holds.