2

why are these declaration invalid in Java?

  1. List<Number> test = new ArrayList<? extends Number>();

  2. List test = new ArrayList<? extends Number>();

are we not allowed to use wildcard during instantiation. and if the wildcards are only useful for passing them to methods?

and List<Object> test = new ArrayList<Integer>(); is illegal because generics are not covariant correct?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
eagertoLearn
  • 9,772
  • 23
  • 80
  • 122

7 Answers7

4

The ? wildcard character means "unknown" not "any". It doesn't make any sense to instantiate a new container of unknown contents, what would you put in there? It can't really be used for anything!

So the declaraion new ArrayList<? extends Number>() Means "some specific thing that extends number, but I don't know what." It does not mean "anything that extends number."
The List<Number> you assigned it to would allow both Double and Integer to be added to it, but the actual contents of a List<? extends Number> might be Float! (or whatever else.) Consider what would happen in this code if the wildcard worked as an "Any":

    List<Integer> listInteger = new ArrayList<Integer>();
    listInteger.add(Integer.valueOf(1));
    List<? extends Number> listWildCard = listInteger;
    listWildCard.add(Double.valueOf(1.0)); //This does not compile
    Integer integer = listInteger.get(1);//because this would throw a ClassCastException

Footnote regarding your second example: Declaring a paramaterized type with no type parameter is called using the Raw Type. This is considered a programming error. The syntax is only legal so that code written before java 5 still compiles. Just don't do it if your scenario isn't backward compatability with pre-java 5.

Affe
  • 47,174
  • 11
  • 83
  • 83
  • whatever extends `Number` is a `Number` type by polymorphism correct? hence whatever could be added to `List would be something that will be of type `List extends Number>`?? – eagertoLearn Sep 04 '13 at 19:57
  • No, `? extends Number` means "This list holds one exact subtype of Number, might be Integer, might be Float, whatever, but I don't know which one" so you can't add anything, since you just told it you don't know which subtype is in the list already! – Affe Sep 04 '13 at 19:59
  • so it is meant to contain only one subtype of Number, I was thinking I could have mix of different subtypes, like Integer, Double etc – eagertoLearn Sep 04 '13 at 20:07
  • 3
    A list that can contain any subtype of Number is just a `List`! The wildcard is 'unknown' not 'any'. – Affe Sep 04 '13 at 20:08
  • Then why is this allowed: `List> list = new ArrayList>();`? – johnnyodonnell Sep 14 '19 at 03:16
  • Because you didn't actually create a List> you just used it as a compile time reference type? – Affe Sep 15 '19 at 20:17
3

To understand why it is not allowed to create objects of wildcard parameterized types, you must first understand what's the use of wildcard parameterized types.

Why wildcards?

As you already know that Java generics are invariant. So a List<Number> is not a super class of List<Integer>, even though their type arguments are covariant. So, what if you want such a behaviour in generics too, like having the same reference pointing to different objects? That polymorphic thing, as you would name it. What if you want a single List reference to refer to list of Integer, Float, or Double?

Wildcards to the rescue:

With wildcards, you can achieve the above mentioned behaviour. So, a List<? extends Number> can refer to a List<Integer>, List<Double>, etc. So, the following declarations are valid:

List<? extends Number> numbers = new ArrayList<Integer>();
numbers = new ArrayList<Double>();
numbers = new ArrayList<Float>();
numbers = new ArrayList<String>();  // This is still not valid (you know why)

So what did we change here? Just the reference type of the numbers. Note that generics were introduced in Java for enforcing stronger compile time check. So, it's primarily the compiler's job to decide whether the declaration of a parameterized type is conforming to the rule or not. Without wildcard, compiler shows you error for a List<Number> refering to List<Integer>.

So, wildcards are just a way to introduce co-variance like behaviour into generics. By using wildcards, you increase the flexibility, or you can say, reduce the restriction that compiler enforces. A List<? extends Number> reference tells the compiler that the list can refer to a list of Number or any subtype of Number(Of course, there are lower bounded wildcards too. But that's not the point here. The differences between them is already discussed in many other answers on SO only).

Major uses of wildcard parameterized type you would see with method parameters, where you want to pass different instantiation of a generic type for a single method parameter:

// Compiler sees that this method can take List of any subtype of Number
public void print(List<? extends Number> numbers) {
    // print numbers
}

But at runtime, for creating an object you have to give a concrete type. A wildcard - bounded, or unbounded, is not a concrete type. ? extends Number could mean anything that is subtype of Number. So what type of List would you expect to be created when you create a List<? extends Number>? You can consider this case similar to the reason why you can't instantiate an interface. Because they aren't just concrete.

There is a workaround. Really?

Although this is illegal, you would be surprised to know that there is a workaround, as explained in - Java Generics FAQs. But I really don't think you would ever need that.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
2

When you instantiate a parameterized class, the parameter has to be some known, concrete type. It can be parameterized class even with ? but for inference reasons it has to be concrete. E.g. this is a valid declaration: new ArrayList<List<?>>();

The trick here is that the methods that use the type parameter in the arguments of their signature require the type of the argument to be lower bound. That is, any parameter that you pass in can be cast to the parameter type. Example:

public void fillUp(List<? super T> param)

The fillUp method takes a collection and fills it with T type objects. The param list must be able to handle the T objects so it is declared that the list can contain types that are ancestors of T, T can be safely cast to that type. If T was not a concrete type, like ? extends Number, then it would be impossible to exactly define all ancestors of T.

1

That's not a valid declaration as it's not a known type. You're not specifying a full type here. new ArrayList<Number> can accept anything that extends Number by subtyping so your use of ? extends Foo is not a valid need.

List<Number> can accept Integer, Long, etc. There's no way to do the equivalent of ? super Foo as it would be semantically meaningless beyond List or List<Object> with a strange artificial restriction.

nanofarad
  • 40,330
  • 4
  • 86
  • 117
1

Your current definition is not true, The generic type should be same in both sides or should be have inheritance relation.

Behzad Hassani
  • 2,129
  • 4
  • 30
  • 51
1

Java generics are not covariant. See, for example, the article Java theory and practice: Generics gotchas by Brian Goetz. Your first example has two problems. First, when you instantiate a type it must be fully specified (including any type parameters). Second, the type parameters must exactly match the left side.

Regarding type covariance (or lack thereof), this is also not legal:

List<Number> test = new ArrayList<Integer>();

despite the fact that Integer extends Number. This also explains why the second example is illegal. A raw type is more or less the same as binding the type parameter to Object, so it would be like:

List<Object> test = new ArrayList<Integer>();

which again fails because generics are not covariant.

As to why the type parameters must be fully specified, the Java Language Specification, §8.1.2 explains the concept:

A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments.

You can only instantiate an actual type. As long as a type parameter of a generic type is unbound, the generic type itself is incomplete. You need to tell the compiler which specific parameterized type (among the set defined by the generic class) is being instantiated.

As to why generics are not covariant, this was intended to prevent the following sorts of errors:

List<Integer> iTest = new ArrayList<Integer>();
List<Number> test = iTest;
test.add(Double.valueOf(2.5));
Integer foo = iTest.get(0); // Oops!
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • Actually OP seems to know about generics invariance. He is interested in knowing why he can't create object of wildcard parameterized types. – Rohit Jain Sep 04 '13 at 20:11
  • @RohitJain - I guess OP does, although perhaps OP doesn't realize how that's relevant here. I also added a bit to my answer regarding trying to instantiate incompletely specified types. – Ted Hopp Sep 04 '13 at 20:21
  • Then why is this allowed: `List> list = new ArrayList>();`? – johnnyodonnell Sep 14 '19 at 03:27
0

Do not get confused by the Java inheritance concept and wildcard (e.g. ? here) syntex of Java generic concept. Both are not the same and none of the inheritance rule applies to java generic concept.

Hence Number is not same as ? extends Number

Please note that Java Generic wildcard is intended to tell the compiler about the intended object use. At runtime, it does not exist at all!

If you see generic in java just as a tool to prevent you to make mistakes, you should not go wrong in understanding this concept.

Gyanendra Dwivedi
  • 5,511
  • 2
  • 27
  • 53
  • can you elaborate how the rules does not apply? when we say `? extends Number`, it means "It is one exact subtype of Number, but I don't know which one" as one of the comments below mentions. hence `? extends Number` has to be subtype of `Number` which is polymorphism (NOT specially inheritance) – eagertoLearn Sep 04 '13 at 20:05
  • You might want to read the link given by Tedd Hopp for more. One such example is that generic does not have anything like covarient assignment. Only because some of the syntax of generic 'looks like' those of inharitance, it does not mean that it is supporting polymorphism and inheritance features. Now, if you ask why such syntax is given, it is to support the code written for a polymorphic behavior at runtime; but is no way for polymorphic generic concept. – Gyanendra Dwivedi Sep 04 '13 at 20:14