Java中未绑定通配符泛型的用途和要点是什么?

发布于 2024-12-08 03:20:25 字数 166 浏览 1 评论 0 原文

我不明白未绑定通配符泛型有什么用。具有上限 非常有意义,因为使用多态性我可以处理该类型或集合。但是拥有可以是任何类型的泛型有什么意义呢?这不是违背了泛型的目的吗?编译器没有发现任何冲突,并且在类型擦除之后,就像没有使用泛型一样。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

撩人痒 2024-12-15 03:20:25

当您的方法并不真正关心实际类型时,未绑定类型可能很有用。

一个原始的例子是这样的:

public void printStuff(Iterable<?> stuff) {
  for (Object item : stuff) {
    System.out.println(item);
  }
}

由于 PrintStream.println() 可以处理所有引用类型(通过调用 toString()),我们不关心 em> Iterable 的实际内容是什么。

并且调用者可以传入ListSetCollection<?扩展 MySpecificObject>

另请注意,根本不使用泛型(称为使用原始类型)会产生完全不同的效果:它使编译器处理整个对象,就好像泛型根本不存在一样。换句话说:不仅类的类型参数被忽略,方法上的所有泛型类型参数也被忽略。

另一个重要的区别是,您不能向 Collection 添加任何(非 null)值,但可以添加全部对象到原始类型Collection

这不会编译,因为c的类型参数是未知类型(=通配符) ,所以我们不能提供一个保证的值可分配给该类型(null 除外,它可分配给所有引用类型)。

Collection<?> c = new ArrayList<String>();
c.add("foo");    // compilation error

如果您保留类型参数(即使用原始类型),则可以将任何内容添加到集合中:

Collection c = new ArrayList<String>();
c.add("foo");
c.add(new Integer(300));
c.add(new Object());

请注意,编译器将警告您不要使用原始类型type,特别是出于这个原因:它删除了与泛型相关的任何类型检查。

An unbound type can be useful when your method doesn't really care about the actual type.

A primitive example would be this:

public void printStuff(Iterable<?> stuff) {
  for (Object item : stuff) {
    System.out.println(item);
  }
}

Since PrintStream.println() can handle all reference types (by calling toString()), we don't care what the actual content of that Iterable is.

And the caller can pass in a List<Number> or a Set<String> or a Collection<? 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 a Collection<?>, but can add all objects to the raw type Collection:

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 for null, which is assignable to all reference types).

Collection<?> c = new ArrayList<String>();
c.add("foo");    // compilation error

If you leave the type parameter out (i.e. use a raw type), then you can add anything to the collection:

Collection c = new ArrayList<String>();
c.add("foo");
c.add(new Integer(300));
c.add(new Object());

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.

微暖i 2024-12-15 03:20:25

当您需要执行 instanceof 检查时。

你不能像这样参数化:

Object value;
if (value instanceof List<String>) {
    // ...
}

所以你这样做:

Object value;
if (value instanceof List<?>) {
    // ...
}

When you need to perform an instanceof check.

You can't parameterize like this:

Object value;
if (value instanceof List<String>) {
    // ...
}

So you do:

Object value;
if (value instanceof List<?>) {
    // ...
}
不打扰别人 2024-12-15 03:20:25

请允许我重新表述一下问题:

ListList 之间有什么区别?”

答案是 List 更具限制性。它告诉我们,我们有一堆 some 类型的对象,但该类型不一定是 Object

由于我们不知道该类型是什么,因此我们根本无法添加到列表中 - 我们添加的任何内容都可能是错误的类型。事实上,我们不能将任何 ? 类型的参数传递给任何方法,而不仅仅是 add()

从好的方面来说,当我们指定一个方法采用 List 时,它可以采用 ListList > 或任何其他ListList只能采用List

Allow me to rephrase the question:

"What is the difference between List<Object> and List<?> ?"

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 necessarily Object.

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 just add().

On the plus side, when we specify that a method takes List<?>, it can take List<String> or List<Integer> or any other List<>. List<Object> can only take List<Object>.

狼性发作 2024-12-15 03:20:25

使用原始类型意味着您不了解泛型(因为您很懒或者代码是很久以前编写的),而使用 意味着您了解泛型并明确强调您的代码可以处理任何类型的对象。

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.

一抹苦笑 2024-12-15 03:20:25

对于未绑定的通配符,有(罕见的)完全正确的用例。 SDK 包含其中一些。

一个例子是一种方法,它对任何类型的列表执行明确的操作,并且不会像 Collections 中的 rotate 返回任何内容:

static void rotate(List<?> list, int distance)

另一个例子是当您想要列出可能的列表时类的构造函数,方法是:

Constructor<?>[] getConstructors()

这里甚至不可能使用泛型,因为根据定义,数组将包含不同的构造函数,每个构造函数都有自己的实际类。相比之下,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 in Collections:

static void rotate(List<?> list, int distance)

Another example is when you want to list the possible constructors for a class, the method is :

Constructor<?>[] getConstructors()

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.

遥远的她 2024-12-15 03:20:25

列表<对象>是一个可以包含任何对象的列表,例如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.

假扮的天使 2024-12-15 03:20:25

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 Objectand 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文