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
Box<?> box = new Box<String>("abc");
box.put("xyz"); // error
String s = box.take(); // error
In OOP, you're using polymorphism. So at compiling time, type of box object is Box<?>
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 :)
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");
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".null
into "box of unknown" ? sure.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).
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
isset
cannot be called at allI 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 ?
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With