Java中的泛型无法编译
我被困在试图了解这里到底发生了什么。使用 copy_v1 方法的两种方式如下所示。第一种方式 (1) 会产生编译错误。但是第二种方式 (2) 不会产生任何编译错误(当我注释掉 (1) 后尝试执行程序时,它也不会产生任何运行时错误)。当我用 替换 (2) 时copy_v1(new Wrapper<String>("Hello"), new Wrapper<Object>(new Object()));,我最终也会在 (2) 上遇到编译错误。不知道发生了什么。(1) 上的代码与 (2) 上的代码有何不同?有人可以对此有所了解吗?
谢谢。
public class SOQuestion {
public static <T> void copy_v1(Wrapper<T> source, Wrapper<T> dest) {
T srcObj = source.getRef();
dest.setRef(srcObj);
}
public static void main(String[] args) {
Wrapper<Object> objectWrapper = new Wrapper<>(new Object());
Wrapper<String> stringWrapper = new Wrapper<>("Hello");
copy_v1(stringWrapper, objectWrapper); // Compile error on this line (1)
copy_v1(new Wrapper<>("Hello"), new Wrapper<>(new Object())); // But no error on this line (2)
}
}
class Wrapper<T> {
private T ref;
Wrapper(T ref) {this.ref = ref;}
public T getRef() {return this.ref;}
public void setRef(T ref) {this.ref = ref;}
}
回答
在 中copy_v1(stringWrapper, objectWrapper);,无法推断Tfor的泛型参数copy_v1。实际上没有有效的类型T,因此调用是有效的。
如果T是String,则第二个参数不能转换为Wrapper<String>。Wrapper<Object>不是一种Wrapper<String>,因为你可以调用setRef(new Object())前者,但不能调用后者。我知道您没有在 中这样做copy_v1,但是编译器不会查看方法中发生的情况来确定调用是否有效。
同样,如果T是Object,则第一个参数不能转换为Wrapper<Object>。Wrapper<String>不是一种Wrapper<Object>。String当您调用getRef前者时,您肯定会得到一个,但不会调用后者。
但是,在 中copy_v1(new Wrapper<>("Hello"), new Wrapper<>(new Object()));,您也要求编译器推断Wrappers的类型参数。在这种情况下,编译器会尽力使您的方法调用有效,并像这样推断:
SOQuestion.<Object>copy_v1(new Wrapper<Object>("Hello"), new Wrapper<Object>(new Object()));
Tforcopy_v1是Object,并且Ts 的Wrappers 都是Object。现在一切都是有效的。new Wrapper<Object>接受一个Object参数,并且"Hello"可以传递给它,因为String继承自Object.
请注意,如果您将您的更改copy_v1为:
public static <T> void copy_v1(Wrapper<? extends T> source, Wrapper<? super T> dest) {
T srcObj = source.getRef();
dest.setRef(srcObj);
}
然后做copy_v1(stringWrapper, objectWrapper);就好了。这限制了您可以使用dest和source在方法内部执行的操作,但允许调用者将例如传递Wrapper<String>到dest应该使用Wrapper<Object>.