0

I'm quite new in Java, although I have much experience in C++ and other languages. So templates/generics are not something I don't know.

There's something that bothers me though, it is this <?> that I was told I should use everytime I use a generic instance of something when I don't know in advance of which specific type it will be:

Like:

List< MyGeneric >    foo; // bad
List< MyGeneric<?> > bar; // good

IntelliJ doesn't barf on me when using the first expression, and I don't understand why it should. My coworkers have expressed that the 2nd expression was much better, but couldn't tell me exactly why.

I mean, what exactly is the difference between these two, apart from the second being explicit about the fact that it is a generic that we manipulate ?

The compiler certainly knows that it is a generic at compile time, so my guess is that the second expression is only better because it tells the programmer that he is manipulating a generic.

Am I right?

Edit: for clarification, I ovbiously use the most restrictive type, like List<MyGeneric<Double>>, whenever I know in advance what I am going to store in there. My question is for when I store unknown types of generics.

Gui13
  • 12,993
  • 17
  • 57
  • 104
  • 2
    "I was told I should use everytime I use a generic instance of something." Shoot the messenger! And... "My coworkers have expressed that the 2nd expression was much better, but couldn't tell me exactly why." You're talking to people who can't back up their arguments. Debate over. – ChiefTwoPencils May 23 '16 at 07:45
  • Simply example of why the first is bad: iterate over the list using `for (MyGeneric obj : foo) {}`, and you have a raw `MyGeneric` instance. – Andy Turner May 23 '16 at 07:49
  • It allows you to pass any type but not null – Pravat Panda May 23 '16 at 07:57

4 Answers4

9

Every time? It's not applicable always, and it doesn't always make sense.

Let's describe what that actually is: <?> is an unbound wildcard, which immediately implies two things:

  • MyGeneric is a generic class, but
  • You do not know what type it's holding (and it likely doesn't matter).

It is preferable to the first expression in that the first expression always guarantees that you'll be working with a raw type, and you really don't want to use raw types. However, it is a gross overgeneralization to assume that using an unbound wildcard every time would be ideal.

If you actually know or care the type, or know or care about its bounds, use that instead.

Community
  • 1
  • 1
Makoto
  • 104,088
  • 27
  • 192
  • 230
  • My question is not clear, I indeed use the *specific* generic type whenever possible (like `List>` when I know I'll only store Doubles). My question is about when I store multiple generics. I'll try to rephrase! – Gui13 May 23 '16 at 07:50
  • 3
    @Gui13: My answer still applies; if you don't know or don't care about the type that the generic is holding, it's fine to use the wildcard. However, in the context that you *do* - like, for instance, iterating over that collection and performing operations on each entry that is context sensitive to its generic type - it's preferable to use the most restrictive type in your case. – Makoto May 23 '16 at 07:54
  • @Gui13 specifically about why use `MyGeneric>` instead of just `MyGeneric` - that's because `MyGeneric` is a raw type, see the link that's also in Makoto's answer: [What is a raw type and why shouldn't we use it?](http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) – Jesper May 23 '16 at 08:05
4

Let's give an example of why it's bad to use the first. Assuming MyGeneric is defined like this:

class MyGeneric<T> {
  private final T instance;
  MyGeneric(T instance) { this.instance = instance; }
  T get() { return instance; }
}

The following code would compile and run, but fail at runtime with a ClassCastException:

List<MyGeneric> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));

for (MyGeneric instance : list) { 
  Integer value = (Integer) instance.get();  // Compiles, but fails at runtime.
}

This compiles because you're using raw types: the compiler doesn't know that instance.get() can't return an Integer; it would merely warn you that it might be unsafe.

On the other hand, the following code would not even compile:

List<MyGeneric<String>> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));

for (MyGeneric<String> instance : list) { 
  Integer value = (Integer) instance.get();  // Won't compile, incompatible types.
}
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
3

The difference is that a raw type ignores the fact that the class is a generic, while the wildcard <?> specifies that the class is a generic but the type argument is unknown.

Raw means that you lose all compiler type-checking. Wildcard keeps type-checking intact.

Example:

public class MyGeneric<T> {
    private T val;
    public T get() {
        return this.val;
    }
    public void set(T val) {
        this.val = val;
    }
}

MyGeneric a = new MyGeneric<Integer>();
a.set("Foo"); // accepted

Setting the value for a to a String when it was declared to be an Integer is accepted by the compiler, because a was defined raw, which means that the compiler is ignoring the fact that the class is a generic. When val is later used as an Integer, the program will crash. It's a bomb waiting to go off.


MyGeneric<?> b = new MyGeneric<Integer>();
b.set("Bar"); // compile error

Trying to set the value for b will not compile:

The method set(capture#1-of ?) in the type MyGeneric<capture#1-of ?> is not applicable for the arguments (String)

Here the compiler knows that the class is a generic and will not allow setting the value to anything (even an Integer), because it doesn't know what type would be allowed (wildcard = unknown, remember?). The compiler safeguards here, as it should.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • THIS. Although the answer of Makoto links to the raw pointer explanation, this is the answer that explains WHY it is important. Thank you Andreas. – Gui13 May 23 '16 at 08:59
0

List<?> means a list typed to an unknown type. This could be a List<A>, a List<B>, a List<String> etc.

Since the you do not know what type the List is typed to, you can only read from the collection, and you can only treat the objects read as being Object instances. Here is an example:

public void processElements(List<?> elements) {
    for(Object o : elements){
        System.out.println(o);
    }
}

The processElements() method can now be called with any generic List as parameter. For instance a List<A>, a List<B>, List<C>, a List<String> etc. Here is a valid example:

List<A> listA = new ArrayList<A>();

processElements(listA);

Following tutorials will further help you to understand it:

dur
  • 15,689
  • 25
  • 79
  • 125
Ms. Zia
  • 411
  • 3
  • 12