我不明白未绑定通配符泛型有什么用。具有上限 非常有意义,因为使用多态性我可以处理该类型或集合。但是拥有可以是任何类型的泛型有什么意义呢?这不是违背了泛型的目的吗?编译器没有发现任何冲突,并且在类型擦除之后,就像没有使用泛型一样。
I don't understand what is the use of unbound wildcards generics. Bound wildcards generics with upper boundary <? extends Animal>
makes perfect sense, because using polymorphism I can work with that type or collection. But what is the point of having generics that can be of any type? Doesn't it defeat the purpose of generics? Compiler doesn't find any conflict and after type erasure it would be like no generics was used.
发布评论
评论(7)
当您的方法并不真正关心实际类型时,未绑定类型可能很有用。
一个原始的例子是这样的:
由于
PrintStream.println()
可以处理所有引用类型(通过调用toString()
),我们不关心 em> Iterable 的实际内容是什么。并且调用者可以传入
List
或Set
或Collection<?扩展 MySpecificObject>
。另请注意,根本不使用泛型(称为使用原始类型)会产生完全不同的效果:它使编译器处理整个对象,就好像泛型根本不存在一样。换句话说:不仅类的类型参数被忽略,方法上的所有泛型类型参数也被忽略。
另一个重要的区别是,您不能向
Collection
添加任何(非null
)值,但可以添加全部对象到原始类型Collection
:这不会编译,因为
c
的类型参数是未知类型(=通配符?
) ,所以我们不能提供一个保证的值可分配给该类型(null
除外,它可分配给所有引用类型)。如果您保留类型参数(即使用原始类型),则可以将任何内容添加到集合中:
请注意,编译器将警告您不要使用原始类型type,特别是出于这个原因:它删除了与泛型相关的任何类型检查。
An unbound type can be useful when your method doesn't really care about the actual type.
A primitive example would be this:
Since
PrintStream.println()
can handle all reference types (by callingtoString()
), we don't care what the actual content of thatIterable
is.And the caller can pass in a
List<Number>
or aSet<String>
or aCollection<? extends MySpecificObject<SomeType>>
.Also note that not using generics (which is called using a raw type) at all has a quite different effect: it makes the compiler handle the entire object as if generics don't exist at all. In other words: not just the type parameter of the class is ignored, but also all generic type parameters on methods.
Another important distinctions is that you can't add any (non-
null
) value to aCollection<?>
, but can add all objects to the raw typeCollection
:This won't compile, because the type parameter of
c
is an unknown type (= the wildcard?
), so we can't provide a value that is guaranteed to be assignable to that (except fornull
, which is assignable to all reference types).If you leave the type parameter out (i.e. use a raw type), then you can add anything to the collection:
Note that the compiler will warn you not to use a raw type, specifically for this reason: it removes any type checks related to generics.
当您需要执行
instanceof
检查时。你不能像这样参数化:
所以你这样做:
When you need to perform an
instanceof
check.You can't parameterize like this:
So you do:
请允许我重新表述一下问题:
“
List
答案是
List
更具限制性。它告诉我们,我们有一堆 some 类型的对象,但该类型不一定是Object
。由于我们不知道该类型是什么,因此我们根本无法添加到列表中 - 我们添加的任何内容都可能是错误的类型。事实上,我们不能将任何
?
类型的参数传递给任何方法,而不仅仅是add()
。从好的方面来说,当我们指定一个方法采用
List
时,它可以采用List
或List
> 或任何其他List
。List
Allow me to rephrase the question:
"What is the difference between
List<Object>
andList<?>
?"The answer to that is that
List<?>
is more restrictive. It tells us that we have a bunch of object of some type, but that type is not necessarilyObject
.Since we don't know what that type is, we cannot add to the list at all - anything we add may be of wrong type. In fact, we cannot pass any argument of
?
type to any method, not justadd()
.On the plus side, when we specify that a method takes
List<?>
, it can takeList<String>
orList<Integer>
or any otherList<>
.List<Object>
can only takeList<Object>
.使用原始类型意味着您不了解泛型(因为您很懒或者代码是很久以前编写的),而使用
意味着您了解泛型并明确强调您的代码可以处理任何类型的对象。
While using raw types means that you don't know about generics (because you're lazy or code was written ages ago), using
<?>
means that you know about generics and explicitly emphasize that your code can work with any kind of objects.对于未绑定的通配符,有(罕见的)完全正确的用例。 SDK 包含其中一些。
一个例子是一种方法,它对任何类型的列表执行明确的操作,并且不会像
Collections
中的rotate
返回任何内容:另一个例子是当您想要列出可能的列表时类的构造函数,方法是:
这里甚至不可能使用泛型,因为根据定义,数组将包含不同的构造函数,每个构造函数都有自己的实际类。相比之下,API 确实使用通用签名来获取单个构造函数:
Constructor; getConstructor(Class...parameterTypes)
。结论是,即使它主要用于与旧代码兼容,但仍然有一些地方未绑定的通配符泛型是正确的方法。
There are (rare) perfectly correct use cases for unbound wildcards. The SDK contains some of them.
One example is a method that does a definite action on a list of any kind and does not return anything as
rotate
inCollections
:Another example is when you want to list the possible constructors for a class, the method is :
Here it in not even possible to use a generic, because by definition the array will contain different constructor each with its own actual class. By contrast, the API does use a generic signature for getting one single constructor :
Constructor<T> getConstructor(Class<?>... parameterTypes)
.The conclusion is that even if it is mainly used for compatibility with older code, there are still places where unbound wildcard generics are the correct way.
列表<对象>是一个可以包含任何对象的列表,例如l[0]可以是整数,l[1]可以是字符串等。如果是List,则仅存储Integers,如果是List,则仅存储String。
列表可以是列表
List<Object> is a List that may contain any Object, e.g. l[0] may be an Integer, l[1] may be a String, etc.
List<?> may be a List<Integer> or List<String>, etc. If it is a List<Integer>, it stores only Integers, if it is List<String>, it stores only Strings.
AFAIK,当包装不使用泛型(基本上是集合)的旧代码时,使用无界通配符才有意义。
如果你看看你能用这样的泛型做什么,你会发现基本上什么都没有。
如果您有一个集合,则无法添加任何内容,如果您尝试读取某些内容,您将始终得到一个
对象
等等。这反过来又有助于保证您将以类型安全的方式处理数据,而使用原始类型会导致编译器忽略您造成的任何混乱。
哪些方法和字段可以通过通配符参数化类型的引用变量访问/不可访问? 来自 Angelika Langers Java 泛型常见问题解答 可能会感兴趣。
Using unbounded wildcards only makes sense, AFAIK, when wrapping old code that is not using generics, basically Collections.
If you look at what you can do with such a generic it's basically nothing.
If you have a collection you can't add anything, if you try to read something out you will always get an
Object
and so on.This in turns helps guaranteeing that you will handle the data in a type safe way, whereas using the raw type would have caused the compiler to ignore any mess you'd make.
Which methods and fields are accessible/inaccessible through a reference variable of a wildcard parameterized type? from Angelika Langers Java Generics FAQ might be of interest.