Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics Wildcard confusion [duplicate]

Tags:

java

generics

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

like image 670
foolhunger Avatar asked Jan 02 '15 03:01

foolhunger


4 Answers

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 :)

like image 151
hqt Avatar answered Oct 22 '22 08:10

hqt


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");
like image 31
Jeff Storey Avatar answered Oct 22 '22 07:10

Jeff Storey


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

like image 44
S.D. Avatar answered Oct 22 '22 09:10

S.D.


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

like image 20
Radiodef Avatar answered Oct 22 '22 09:10

Radiodef