doctorgester
doctorgester

Reputation: 85

Code with generics won't compile

I've failed to google this problem. Why would this line produce a compilation error.

wrapper.doSmth(wrapper.getCurrent());

I'm using java 7.

public class App {
 Wrapper<?> wrapper;

 class Generic<T>{

 }

 class Wrapper<T>{
  Generic<T> current;

  public void doSmth(Generic<T> generic){
  }

  public Generic<T> getCurrent(){
   return current;
  }
 }

 public void operation(){
  wrapper.doSmth(wrapper.getCurrent());
 }
}

The error is:

Error:(25, 24) java: method doSmth in class App.Wrapper<T> cannot be applied to given types;
  required: App.Generic<capture#1 of ?>
  found: App.Generic<capture#2 of ?>
  reason: actual argument App.Generic<capture#2 of ?> cannot be converted to conf.App.Generic<capture#1 of ?> by method invocation conversion

Upvotes: 5

Views: 114

Answers (2)

rgettman
rgettman

Reputation: 178253

The compilation error should be something along the lines of "capture of ? #1 is not compatible with capture of ? #2". The cause of this error is that wrapper is a Wrapper<?>.

The compiler sees that the wrapper.getCurrent() returns a Generic<?>, and that wrapper.doSmth takes a Generic<?> as a parameter. But it won't equate the two ? wildcards, even if we can see that they come from the same instance and should be the same.

One solution here is to make the App class generic, so you can replace the wildcard.

public class App<T> {

Because of the fact that Generic and Wrapper are inner classes, T is still in scope, so you don't need to declare a generic type parameter for them any more.

    Wrapper wrapper;

    class Generic{

    }

    class Wrapper{
        Generic current;

        public void doSmth(Generic generic){
        }

        public Generic getCurrent(){
            return current;
        }
    }

    public void operation(){
        wrapper.doSmth(wrapper.getCurrent());
    }
}

Upvotes: 8

Radiodef
Radiodef

Reputation: 37845

Could be a job for a capturing helper.

public void operation() {
    operationImpl(wrapper);
}

private static <T> void operationImpl(Wrapper<T> wrapper) {
    wrapper.doSmth(wrapper.getCurrent());
}

No other change required. The helper captures the type of wrapper so we can be sure getCurrent returns the same type as doSmth accepts.


The reason this error happens is that each time a type with a wildcard is referred to, a distinct type is assumed for that specific point in the expression (called 'capture'):

    Wrapper<?> wrapper = ...;

//  each capture for T is assumed distinct from each other
//  vvvvvvv        vvvvvvv
    wrapper.doSmth(wrapper.getCurrent());

The fact that the references point to the same instance is irrelevant to the way capture is specified. The compiler is not required to account for that and something like this can also happen

Wrapper<?> wrapper = new Wrapper<String>();
wrapper.doSmth((wrapper = new Wrapper<Float>()).getCurrent());

where T can change mid-expression.

Upvotes: 1

Related Questions