Philip Guin
Philip Guin

Reputation: 1460

Generics and Wildcards: Java likes "new Foo<Bar<?>>"

Alright, so Java doesn't allow the following:

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:

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. But if Java is okay with some specificity, why doesn't it allow this?:

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? Why aren't bounded wildcards allowed if nested wildcards are okay?

Upvotes: 7

Views: 229

Answers (4)

newacct
newacct

Reputation: 122439

new Foo<?> can be equivalently replaced with new Foo<SomeRandomTypeIMadeUp> where SomeRandomTypeIMadeUp is any type that satisfies the bound of that type parameter. The simplest choice is to simply to pick the upper bound of that type parameter, e.g. if it is class Foo<T extends X>, then new Foo<X> would suffice.

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

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. 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<?>; this would be pretty useless -- you cannot put any element except null into it.

This is why you are not allowed to do new Foo<?>: It is utterly useless; 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>.

Foo<Bar<?>> is very different. Bar<?> is a specific type. Foo<Bar<?>> does not mean you can assign Foo<Bar<Something>> to it; rather, that is illegal; the type parameters of Foo must match if they are not wildcards. Also unlike with a wildcard, with a List<Bar<?>>, you can put objects into it and take objects out of it.

Upvotes: 1

MvG
MvG

Reputation: 60858

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. The last example could likewise be new Foo<Mak>(), as there is nothing you can do on a Foo<? extends Mal> that you cannot do on a Foo<Mal>. Note that the converse isn't true: a Foo<Mal> might accept Mal arguments where a Foo<? extends Mal> doesn't.

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. This would be the case if you only access methods of Bar which don't rely on the type argument. There is nothing for the compiler to complain here.

Upvotes: 5

Jeffrey
Jeffrey

Reputation: 44808

The reason your first and third declarations don't work is because of 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).

In your first declaration, ? is a wildcard type. In your third declaration, ? extends Mal is a wildcard type.

The reason your second declaration does work is because Bar<?> is not a wildcard type. Bar<?>'s type is Bar.

What do you mean by the "wildcard inside a class parameter of a constructor"?

Upvotes: 3

Benoit
Benoit

Reputation: 1993

When you instanciate an instance of a parameterized type, you must specify a type. ? and ? extends Mal are not types. Bar<?> is a type. 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.

Upvotes: 1

Related Questions