带有泛型的代码无法编译

Code with generics won't compile

我没能google这个问题。为什么这一行会产生编译错误。

wrapper.doSmth(wrapper.getCurrent());

我正在使用 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());
 }
}

错误是:

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

编译错误应该与 "capture of ? #1 is not compatible with capture of ? #2" 类似。这个错误的原因是 wrapper 是一个 Wrapper<?>.

编译器看到 wrapper.getCurrent() returns 一个 Generic<?>,并且 wrapper.doSmth 接受一个 Generic<?> 作为参数。但它不会将两个 ? 通配符等同起来,即使我们可以看到它们来自同一个实例并且应该是相同的。

此处的一个解决方案是使 App class 通用,这样您就可以替换通配符。

public class App<T> {

因为GenericWrapper是内部classes,T还在范围内,所以你不需要声明泛型不再为它们输入参数。

    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());
    }
}

可能是 capturing helper 的工作。

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

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

无需进行其他更改。助手捕获 wrapper 的类型,因此我们可以确定 getCurrent returns 与 doSmth 接受的类型相同。


发生此错误的原因是每次引用带有通配符的类型时,都会为表达式中的特定点(称为 'capture')假定一个不同的类型:

    Wrapper<?> wrapper = ...;

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

引用指向同一个实例的事实与指定捕获的方式无关。编译器不需要考虑这一点,这样的事情也可能发生

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

其中 T 可以更改中间表达式。