Collections.copy
定义为:
public static <T> void copy(List<? super T> dest, List<? extends T> src)
我理解它是如何工作的,没有任何问题。我尝试用另一种方式展示相同的:
public static <T, E extends T> void copy(List<T> dest, List<E> src){
for(E e: src){
dest.add(e);
}//ignore implementation. it has flaws. The focus is declaration of copy
}
这个想法很简单。对于从源读取的任何类型,目标类型应为其超类型。所以现在你可以这样做:
copy(new ArrayList<Number>(), new ArrayList<Number>());
copy(new ArrayList<Number>(), new ArrayList<Integer>());
copy(new ArrayList<Object>(), new ArrayList<Number>());
copy(new ArrayList<Object>(), new ArrayList<Double>());
看起来不错。但与实际Collections.copy相比,上面是否有任何缺陷?从类型信息的角度来看,实际上超出上述地方的任何地方?
答案 0 :(得分:3)
我认为原件更好的一个地方是简洁。特别是从代码生成的角度来看。
如果我通过完整声明调用原始函数,我可以
Collections.<Object>copy(new ArrayList<Object>(), new ArrayList<Number>());
就我而言,它将是:
MyClass.<Object, Number> copy(new ArrayList<Object>(), new ArrayList<Number>());
所以它更简洁一点。
答案 1 :(得分:2)
就绝对信息类型而言,我认为没有区别(但我很乐意被证明是错误的)。实际上,如果静态方法的主体只是对Collections.copy
的调用,它会编译并运行正常。经过大量的思考和实验后,有几个关键点:
Collections.copy
的签名在概念上比您建议的签名要清晰得多。它举例说明了PECS principle(另见Josh Bloch的 Effective Java )。Collections.copy
的源代码(OpenJDK 7),我发现类型参数T
实际上从未直接使用过。您的版本创建了相同的类型边界(源必须生成dest的子类型),但没有这样一个明确的含义,即通配符边界会遇到一些明确定义的“中间类型”。考虑一个复制参数化类型的类TypeCopier<T>
。
public class TypeCopier<T> {
void copyType(List<? super T> dest, List<? extends T> src) {
// copy
}
public static void main(String[] args) {
TypeCopier<Number> copier = new TypeCopier<>();
copier.copyType(new ArrayList<Object>(), new ArrayList<Integer>());
}
}
请注意关于第一个示例的一些事项:
Number
很容易
dest
和src
分别是Number
的任何超类型和子类型现在尝试使用您建议的签名创建类似的类:
public class TypeCopierBad<T, E extends T> {
void copyType(List<T> dest, List<E> src) {
// copy
}
public static void main(String[] args) {
TypeCopierBad<Object, Number> copier = new TypeCopierBad<>();
copier.copyType(new ArrayList<Object>(), new ArrayList<Integer>());
}
}
请注意关于第二个例子的一些事项:
src
不完全是Number
类型dest
和src
的内容答案 2 :(得分:1)
看起来你的声明符合 PECS 原则的要求,所以我认为这只是风格,其中Collections.copy
是更常见的版本。
答案 3 :(得分:0)
假设两个ArrayLists中都有对象。然后迭代源并将每个对象从源添加到目标。问题是,从源获取并添加到目标的每个对象都将指向相同的内存位置。 例如, 说
source {x, y}
destination {x, y}.
在这种情况下,源和目标中的x将指向相同的内存位置