我正在尝试通过阅读以下内容来学习Java Generics通配符: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ103
材料中有一个例子:
public class Collections {
public static <T> void copy (List<? super T> dest, List<? extends T> src) {
for (int i=0; i<src.size(); i++)
dest.set(i,src.get(i));
}
}
我想知道我是否可以按以下方式更改方法签名:
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
↓
public static <T> void copy(List<T> dest, List<? extends T> src) {
这两种方法是否存在差异?
示例将不胜感激。
答案 0 :(得分:5)
你是对的。在这种情况下,两个参数的Type Arguments用于表示dest
必须包含src
中超类型对象的对象的关系。因此,如果您说src
包含<? extends T>
,则表示dest
包含T的对象就足够了。
你也可以反过来表达,即:
List<? super T> dest, List<T> src
达到同样的效果。
编辑:我怀疑作者强调了关于PECS principle
的观点答案 1 :(得分:5)
作为matt freake pointed out in his answer,
之间没有太大的实际区别public static <T> void copyA(List<? super T> dest, List<? extends T> src) // and
public static <T> void copyB(List< T> dest, List<? extends T> src)
下面的代码段包含exampleShowingThatTheyAreBasicallyEquivalent
。
作者选择使用? super T
的原因很可能是他们想要强调PECS principle: Producer extends
- Consumer super
。
在此示例中,第一个列表是消费者对象。它只接收来自其他列表的对象。因此,其类型应为List<? super T>
。
但是,下面的代码段还包含exampleShowingOneSubtleDifference
。我几乎无法想到这实际上与相关的情况,只是指出它:当你规避类型时推理,并将类型<T>
固定到一个特定类型,您仍然可以传入List<? super T>
作为第一个方法的第一个参数。在第二个中,类型必须完全匹配 - 但这只是方法签名所说的,所以也许它很明显......
import java.util.List;
public class PecsExample
{
public static void exampleShowingOneSubtleDifference()
{
List<? super Number> superNumbers = null;
List<Number> numbers = null;
PecsExample.<Number>copyA(superNumbers, numbers); // Works
//PecsExample.<Number>copyB(superNumbers, numbers); // Does not work
}
public static void exampleShowingThatTheyAreBasicallyEquivalent()
{
List<? super Object> superObjects = null;
List<? super Number> superNumbers = null;
List<? super Integer> superIntegers = null;
List<Object> objects = null;
List<Number> numbers = null;
List<Integer> integers = null;
List<? extends Object> extendsObjects = null;
List<? extends Number> extendsNumbers = null;
List<? extends Integer> extendsIntegers = null;
copyA(objects, objects);
copyA(objects, numbers);
copyA(objects, integers);
copyA(numbers, numbers);
copyA(numbers, integers);
copyA(integers, integers);
copyA(superObjects, objects);
copyA(superObjects, numbers);
copyA(superObjects, integers);
copyA(superNumbers, numbers);
copyA(superNumbers, integers);
copyA(superIntegers, integers);
copyA(objects, extendsObjects);
copyA(objects, extendsNumbers);
copyA(objects, extendsIntegers);
copyA(numbers, extendsNumbers);
copyA(numbers, extendsIntegers);
copyA(integers, extendsIntegers);
copyB(objects, objects);
copyB(objects, numbers);
copyB(objects, integers);
copyB(numbers, numbers);
copyB(numbers, integers);
copyB(integers, integers);
copyB(superObjects, objects);
copyB(superObjects, numbers);
copyB(superObjects, integers);
copyB(superNumbers, numbers);
copyB(superNumbers, integers);
copyB(superIntegers, integers);
copyB(objects, extendsObjects);
copyB(objects, extendsNumbers);
copyB(objects, extendsIntegers);
copyB(numbers, extendsNumbers);
copyB(numbers, extendsIntegers);
copyB(integers, extendsIntegers);
}
public static <T> void copyA(List<? super T> dest, List<? extends T> src)
{
for (int i = 0; i < src.size(); i++)
{
dest.set(i, src.get(i));
}
}
public static <T> void copyB(List<T> dest, List<? extends T> src)
{
for (int i = 0; i < src.size(); i++)
{
dest.set(i, src.get(i));
}
}
}