views:

534

answers:

2

Hello,

I have a question about Generics in Java, namely using wildcards. I have an example class GenClass like this:

public class GenClass<E> {

    private E var;

    public void setVar(E x) {
     var = x;
    }

    public E getVar() {
     return var;
    }
}

I have another simple class:

public class ExampleClass {

}

I have written the following test class:

public class TestGenClass {

    public static void main(String[] str) {

     ExampleClass ec = new ExampleClass();

     GenClass<ExampleClass> c = new GenClass<ExampleClass>();

     c.setVar(ec);
     System.out.println(c.getVar());  // OUTPUT: ExampleClass@addbf1
    }

}

Now, if I use a wildcard and write in the test class this:

GenClass<?> c = new GenClass<ExampleClass>();

on the place of:

GenClass<ExampleClass> c = new GenClass<ExampleClass>();

the compiler has no problem with this new statement, however, it complains about

c.setVar(ec);

It says that "the method (setVar()) is not applicable for the arguments (ExampleClass)". Why do I get this message?

I thought that the way I have used the wildcard, makes the reference variable c be of type GenClass, which would accept as parameter any class - on the place of E I would have any class. This is just the declaration of the variable. Then I initialize it with

new GenClass<ExampleClass>()

which means that I create an object of type GenClass, which has as parameter a class of type ExampleClass. So, I think that now E in GenClass will be ExampleClass, and I would be able to use the method setVar(), giving it as argument something of type ExampleClass. This was my assumption and understanding, but it seems that Java does not like it, and I am not right. Any comment is appreciated, thank you.

+11  A: 

This exact situation is covered in the Java Generics Tutorial.

Notice that [with the wildcard], we can still read elements from [the generic Collection] and give them type Object. This is always safe, since whatever the actual type of the collection, it does contain objects. It isn't safe to add arbitrary objects to it however:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

(emphasis mine)

Michael Myers
+2  A: 

mmyers has the correct answer, but I just wanted to comment on this part of your question (which sounds like your rationale for wanting to use the wildcard):

I thought that the way I have used the wildcard, makes the reference variable c be of type GenClass, which would accept as parameter any class - on the place of E I would have any class. This is just the declaration of the variable. Then I initialize it with

If you really want to accomplish this, you could do something like without compilation errors:

GenClass<Object> gc = new GenClass<Object>();
gc.setVar(new ExampleClass());

But then again, if you want to declare an instance of GenClass that can contain any type, I'm not sure why you'd want to use generics at all - you could just use the raw class:

GenClass raw = new GenClass();
raw.setVar(new ExampleClass());
raw.setVar("this runs ok");
matt b