[英]What is the use and point of unbound wildcards generics in Java?
我不明白未绑定通配符泛型的用途是什么。 上限为<? extends Animal>
绑定通配符泛型 <? extends Animal>
非常有意义,因为使用多态性我可以处理该类型或集合。 但是拥有可以是任何类型的泛型有什么意义呢? 它不会违背泛型的目的吗? 编译器没有发现任何冲突,并且在类型擦除之后就像没有使用泛型一样。
当您的方法实际上并不关心实际类型时,未绑定类型可能会很有用。
一个原始的例子是这样的:
public void printStuff(Iterable<?> stuff) {
for (Object item : stuff) {
System.out.println(item);
}
}
由于PrintStream.println()
可以处理所有引用类型(通过调用toString()
),所以我们不在乎该Iterable
的实际内容是什么。
呼叫者可以传入List<Number>
或Set<String>
或Collection<? extends MySpecificObject<SomeType>>
Collection<? extends MySpecificObject<SomeType>>
。
还要注意,根本不使用泛型(使用原始类型进行调用)会产生完全不同的效果:这会使编译器处理整个对象 ,就好像泛型根本不存在一样 。 换句话说:不仅会忽略类的类型参数,还会忽略方法上的所有泛型类型参数。
另一个重要的区别是您不能将任何(非null
)值添加到Collection<?>
,但可以将所有对象添加到原始类型Collection
:
这不会编译,因为c
的类型参数是未知类型(=通配符?
),所以我们不能提供保证可分配给该值的值( null
除外,该值可分配给所有引用)类型)。
Collection<?> c = new ArrayList<String>();
c.add("foo"); // compilation error
如果不使用type参数(即使用原始类型),则可以向集合中添加任何内容 :
Collection c = new ArrayList<String>();
c.add("foo");
c.add(new Integer(300));
c.add(new Object());
请注意,编译器会警告您不要使用原始类型,尤其是出于这个原因:它会删除所有与泛型相关的类型检查。
当您需要执行instanceof
检查时。
您不能像这样进行参数化:
Object value;
if (value instanceof List<String>) {
// ...
}
所以你也是:
Object value;
if (value instanceof List<?>) {
// ...
}
对于绑定的通配符,有(很少)完全正确的用例。 SDK包含其中一些。
一个示例是一种对任何类型的列表执行确定操作并且不返回Collections
rotate
的任何方法的方法:
static void rotate(List<?> list, int distance)
另一个示例是当您要列出类的可能构造函数时,方法是:
Constructor<?>[] getConstructors()
在这里甚至不可能使用泛型,因为根据定义,数组将包含不同的构造函数,每个构造函数都有自己的实际类。 相比之下,API确实使用通用签名来获取一个构造函数: Constructor<T> getConstructor(Class<?>... parameterTypes)
。
结论是,即使它主要用于与较早的代码兼容,但仍有某些地方使用未绑定的通配符泛型是正确的方法。
请允许我重新表述这个问题:
“ List<Object>
和List<?>
什么区别?”
答案是List<?>
更具限制性。 它告诉我们,我们有一堆某种类型的对象,但是该类型不一定是Object
。
由于我们不知道该类型是什么,因此我们根本无法添加到列表中-我们添加的任何内容都可能是错误的类型。 实际上,我们不能传递?
任何参数?
输入任何方法,而不仅仅是add()
。
从好的方面来说,当我们指定方法采用List<?>
,它可以采用List<String>
或List<Integer>
或任何其他List<>
。 List<Object>
只能采用List<Object>
。
虽然使用原始类型意味着您不了解泛型(因为您很懒或者代码是很久以前编写的),但是使用<?>
意味着您了解泛型并明确强调您的代码可以与任何类型的对象一起使用。
List<Object> 是一个可以包含任何对象的列表,例如 l[0] 可能是一个整数,l[1] 可能是一个字符串等。 List<?> 可能是一个 List<Integer> 或 List<String>等。如果是List<Integer>,则只存储Integers,如果是List<String>,则只存储Strings。
在包装不使用泛型的旧代码(基本上是Collections)时,使用无界通配符才有意义,AFAIK。
如果您看一下使用这种泛型可以做什么,那么基本上没有什么 。 如果您有一个收藏集,那么您将无法添加任何内容;如果您尝试读取某些内容,则总会得到一个Object
,依此类推。
反过来,这有助于确保您将以类型安全的方式处理数据,而使用原始类型将导致编译器忽略您可能造成的任何混乱。
通过通配符参数化类型的引用变量可访问/不可访问哪些方法和字段? 来自Angelika Langers的Java泛型常见问题解答可能是您感兴趣的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.