7

I'm learning Java Generics recently, and just trying to go though "Java Generics FAQ".

Below question (#304) regarding wildcard parameterized type kinda confused me, would appreciate your help.

Code Example:

class Box<T> { 
  private T t; 
  public Box(T t) { this.t = t; } 
  public void put(T t) { this.t = t;} 
  public T take() { return t; } 
  public boolean equalTo(Box<T> other) { return this.t.equals(other.t); } 
  public Box<T> copy() { return new Box<T>(t); } 
}

class Test { 
  public static void main(String[] args) { 
    Box<?> box = new Box<String>("abc");
    box.put("xyz");     // error 
    box.put(null);     // ok

    String s = box.take();  // error 
    Object o = box.take();  // ok

    boolean equal = box.equalTo(box);  // error {confused}
    equal = box.equalTo(new Box<String>("abc")); // error {confused}

    Box<?> box1 = box.copy();   // ok 
    Box<String> box2 = box.copy();  // error 
  } 
}

Can not figure out why below two method called will fail:

boolean equal = box.equalTo(box);
equal = box.equalTo(new Box<String>("abc"));

Thanks

foolhunger
  • 345
  • 2
  • 12
  • 4
    What does the error say and what don't you understand about it? – Sotirios Delimanolis Jan 02 '15 at 03:08
  • @SotiriosDelimanolis just thought the parameter for equalTo() will be Box>, and don't understand why Box> cannot accept "box" and "new Box("abc")" as argument. – foolhunger Jan 02 '15 at 03:13
  • When you say error, do you mean compiler error or runtime error? – smac89 Jan 02 '15 at 03:14
  • @Smac89 compilation error for all cases in this example – foolhunger Jan 02 '15 at 03:15
  • 1
    Martin Odersky once wrote in an email to the Scala mailinglist that he believes that only three people in the world understand Java Generics. And he should know, after all, he designed them. (Note that I got the impression from the tone of the email that he does not include himself among those who understand them.) So, you shouldn't feel bad about it :-D – Jörg W Mittag Jan 02 '15 at 12:43
  • @JörgWMittag good to know this story :) – foolhunger Jan 05 '15 at 07:18

4 Answers4

7
Box<?> box = new Box<String>("abc");
box.put("xyz");     // error 
String s = box.take();  // error 
  1. In OOP, you're using polymorphism. So at compiling time, type of box object is Box<?>

  2. Type<?> This is called unknown wildcard. Because you don't know what type of Box is typed to, you can only read from that object, and you can only use the objects read as being Object instances. That's why box.put("xyz") is received error and String s = box.take() is received error.

Secondly:

boolean equal = box.equalTo(box);

The equalTo is received Box<T> not Box<?>. As I explained above, T stand for any class, but ? just meaning unknown wildcard, those two terms aren't the same.

And other point you should know. In Java, generic is at compile-time. Contrast to other such as C#, generic is at run-time.

Here is a reference link about wildcard: Java wildcard

Hope this help :)

nhahtdh
  • 55,989
  • 15
  • 126
  • 162
hqt
  • 29,632
  • 51
  • 171
  • 250
5

The equalTo method takes a Box<T>, not Box<?>. When you have an object of type Box<?>, you cannot put Box<String> or even Box<?> as the parameter to equalTo since the T types of the two boxes may not be the same.

Remember that the compiler will use the static type of the object at compile time.

So, this will fail:

Box<?> b = new Box<String>();
b.equalTo(new Box<String>("abc");

But this will not:

Box<String> b = new Box<String>();
b.equalTo(new Box<String>("abc");
Jeff Storey
  • 56,312
  • 72
  • 233
  • 406
5
  • Box<?> is "a box of unknown".
  • Box<? extends String> would be "a box of things that are at least Strings"
  • Box<String> is definitely "a box of strings".
  • So if we have a "box of unknown", and we try to put a type (say a String) in it. Compiler isn't sure. Its a box of unknown. What if it actually only accepted Integers ?
  • Can we put a null into "box of unknown" ? sure.
  • What do we "get()" from a "box of unknown" ? at least an Object.

Given that,

Box<?> box = new Box<String>("abc");

Its asking for compiler to forget about <String> and assume box is "a box of unknown". The errors you see, point that compiler can no longer do type checking of the arguments. It can't check that given type belongs to the unknown or not (except for null).

S.D.
  • 29,290
  • 3
  • 79
  • 130
3

Can not figure out why below two method called will fail

They fail because this is how the wildcard works.

The wildcard represents "some type that we don't know about anymore".

Let's make a simpler class.

class Holder<T> {
    private T obj;

    void set(T obj) {
        this.obj = obj;
    }

    T get() {
        return obj;
    }
}

When we have a Holder<?>, we have special rules for how we can access it. In particular, we cannot call methods with a generic argument. This is because we don't know what type the is anymore: doing so would be unsafe.

When we have a Holder<?> we have something like (conceptually):

class Holder<?> {
    private X obj;

    void set(X obj) {
        this.obj = obj;
    }

    Object get() {
        return obj;
    }
}

Where X means the type is off-limits to us because we don't know what it is anymore.

  • get returns Object because that is the only class we can be sure obj is
  • set cannot be called at all

I suppose this might seem strange because if your equalTo was originally declared like

public boolean equalTo(Box<?> other);

then you would be able to call it. However, the wildcard doesn't work by simply replacing T with ?.

Radiodef
  • 37,180
  • 14
  • 90
  • 125