简体   繁体   English

泛型和通配符:Java喜欢“新的Foo <Bar<?> &gt;”

[英]Generics and Wildcards: Java likes “new Foo<Bar<?>>”

Alright, so Java doesn't allow the following: 好吧,所以Java不允许以下内容:

Foo<?> hello = new Foo<?>();

This makes sense-- after all, what's the point of generics if you're just gonna box/unbox everything anyways? 这是有道理的 - 毕竟,如果你只是想打包/拆箱一切,那么仿制药有什么意义呢?

What's weird, is Java does allow this: 奇怪的是,Java 确实允许这样做:

Foo<Bar<?>> howdy = new Foo<Bar<?>>();

Granted, this actually accomplishes more, though at some point, there would be a cast to get whatever Bar is working with. 当然,这实际上可以实现更多,但在某些时候,会有一个演员来获得Bar正在使用的任何东西。 But if Java is okay with some specificity, why doesn't it allow this?: 但是如果Java有一定的特殊性,为什么它不允许这样做?:

Foo<? extends Mal> bonjour = new Foo<? extends Mal>();

The only reason I ask is I'm fixing to rely on the "wildcard inside a class parameter of a constructor", and would seriously like to know the implications/intent behind it. 我问的唯一原因是我正在修复依赖于“构造函数的类参数中的通配符”,并且非常想知道它背后的含义/意图。

EDIT: To clarify my question, on what grounds are these statements allowed/disallowed? 编辑:澄清我的问题,这些陈述允许/不允许的理由是什么? I'm aware that "Java doesn't permit wildcards in constructors", but the question is, why all this weirdness? 我知道“Java不允许在构造函数中使用通配符”,但问题是, 为什么这些奇怪呢? Why aren't bounded wildcards allowed if nested wildcards are okay? 如果嵌套通配符可以,为什么不允许有界通配符?

As for the rationale: new Foo<?> should probably better be written as new Foo<Object> , so the compiler restriction here forces to write the code as readable as you can. 至于基本原理: new Foo<?>应该最好写成new Foo<Object> ,因此这里的编译器限制强制将代码编写为尽可能可读。 The last example could likewise be new Foo<Mak>() , as there is nothing you can do on a Foo<? extends Mal> 最后一个例子同样可能是new Foo<Mak>() ,因为在Foo<? extends Mal>上你什么也做不了Foo<? extends Mal> Foo<? extends Mal> that you cannot do on a Foo<Mal> . Foo<? extends Mal>你不能在Foo<Mal> Note that the converse isn't true: a Foo<Mal> might accept Mal arguments where a Foo<? extends Mal> 请注意,反之亦然: Foo<Mal>可能接受Mal参数,其中Foo<? extends Mal> Foo<? extends Mal> doesn't. Foo<? extends Mal>没有。

On the other hand, you really might want a Foo object which can handle Bar objects of any kind, so Foo<Bar<?>> makes perfect sense. 另一方面,你真的可能想要一个可以处理任何类型的Bar对象的Foo对象,所以Foo<Bar<?>>非常有意义。 This would be the case if you only access methods of Bar which don't rely on the type argument. 如果您只访问不依赖于类型参数的Bar方法,则会出现这种情况。 There is nothing for the compiler to complain here. 编译器没有什么可以抱怨的。

The reason your first and third declarations don't work is because of JLS §15.9 : 您的第一个和第三个声明不起作用的原因是因为JLS§15.9

It is a compile-time error if any of the type arguments used in a class instance creation expression are wildcard type arguments ( §4.5.1 ). 如果类实例创建表达式中使用的任何类型参数是通配符类型参数(第4.5.1节 ),则为编译时错误。

In your first declaration, ? 在你的第一份声明中, ? is a wildcard type. 是一个通配符类型。 In your third declaration, ? extends Mal 在你的第三份声明中, ? extends Mal ? extends Mal is a wildcard type. ? extends Mal是一种通配符类型。

The reason your second declaration does work is because Bar<?> is not a wildcard type. 你的第二个声明工作的原因是因为Bar<?>是不是一个通配符类型。 Bar<?> 's type is Bar . Bar<?>的类型是Bar

What do you mean by the "wildcard inside a class parameter of a constructor"? 你是什​​么意思“构造函数的类参数中的通配符”?

When you instanciate an instance of a parameterized type, you must specify a type. 在实例化参数化类型的实例时,必须指定类型。 ? and ? extends Mal ? extends Mal ? extends Mal are not types. ? extends Mal不是类型。 Bar<?> is a type. Bar<?>是一种类型。 You can find more information on this page . 您可以在此页面上找到更多信息。

Here is an example for your second case: 以下是您的第二个案例:

Bar<Object> bar1 = ...;
Bar<String> bar2 = ...;
List<Bar<?>> list = new ArrayList<Bar<?>>();
list.add(bar1);
list.add(bar2);

You can imagine to store Bar instances in a list, but you do not need to know their type parameters. 您可以想象将Bar实例存储在列表中,但您不需要知道它们的类型参数。

new Foo<?> can be equivalently replaced with new Foo<SomeRandomTypeIMadeUp> where SomeRandomTypeIMadeUp is any type that satisfies the bound of that type parameter. new Foo<?>可以等效地替换为new Foo<SomeRandomTypeIMadeUp> ,其中SomeRandomTypeIMadeUp是满足该类型参数范围的任何类型。 The simplest choice is to simply to pick the upper bound of that type parameter, eg if it is class Foo<T extends X> , then new Foo<X> would suffice. 最简单的选择是简单地选择该类型参数的上限,例如,如果它是class Foo<T extends X> ,那么new Foo<X>就足够了。

You may ask, why is it that I can just choose any arbitrary type parameter, even one that may have absolutely no connection to the rest of my program? 你可能会问,为什么我可以选择任意类型参数,即使是一个可能与我程序的其余部分完全没有关联的参数? Isn't that unsafe? 这不安全吗? The answer is no, because that's precisely what Foo<?> means -- the type parameter can be anything, and you cannot depend on what it is. 答案是否定的,因为这正是Foo<?>意思 - 类型参数可以是任何东西,你不能依赖它是什么。 This demonstrates the sheer absurdity of what you're asking to do. 这表明了你要做的事情的纯粹荒谬。 Something created with new Foo<?> would be pretty much completely useless, because you cannot do anything with it that depends on the type of the type parameter. new Foo<?>创建的东西几乎完全没用,因为你不能对它做任何取决于类型参数类型的事情。

Types with wildcards are generally useful. 带通配符的类型通常很有用。 For example, you can have an argument of type List<?> and you can pass any type of List to it, and it simply gets stuff out of the list. 例如,你可以有一个List<?>类型的参数,你可以将任何类型的List传递给它,它只是从列表中获取东西。 But in that case, you are not creating the list. 但在这种情况下,您不是在创建列表。 The function that created the list and passed it to you probably had some non-wildcard type parameter. 创建列表并将其传递给您的函数可能有一些非通配符类型参数。 In the scope of that function, you can still put things into the list and do useful things with it. 在该函数的范围内,您仍然可以将事物放入列表中并使用它执行有用的操作。 If a function were to create a List<?> ; 如果函数是创建List<?> ; this would be pretty useless -- you cannot put any element except null into it. 这将是没用的 - 你不能把除null之外的任何元素放入其中。

This is why you are not allowed to do new Foo<?> : It is utterly useless; 这就是为什么你不被允许做new Foo<?> :它完全没用; you are probably using Generics wrong if you want to use it. 如果你想使用它,你可能错误地使用了泛型。 And in the extremely rare case you actually want it, there is a ready substitute, new Foo<AnyTypeThatSatisfiesTheBounds> . 在非常罕见的情况下,你真的想要它,有一个现成的替代品, new Foo<AnyTypeThatSatisfiesTheBounds>

Foo<Bar<?>> is very different. Foo<Bar<?>>非常不同。 Bar<?> is a specific type. Bar<?>特定类型。 Foo<Bar<?>> does not mean you can assign Foo<Bar<Something>> to it; Foo<Bar<?>>并不意味着你可以为它分配Foo<Bar<Something>> ; rather, that is illegal; 相反,这是非法的; the type parameters of Foo must match if they are not wildcards. 如果Foo的类型参数不是通配符,则它们必须匹配。 Also unlike with a wildcard, with a List<Bar<?>> , you can put objects into it and take objects out of it. 与通配符不同,使用List<Bar<?>> ,您可以将对象放入其中并从中取出对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM