this text is best read in a fixed width terminal
C++ compiles code for each required parameterized type
Java compiles only one instance of the parameterized type, using Object as the type
because generic types in java erase information on type
and still the type must be verified by the compiler
there are cases when the code should compile but it doesn't
the solution was to relax type checks at compile time
the '?' wildcard can only appear on the left hand side of an assignment
and only when the variable or parameter is defined
it imposes constraints on the right hand side of the assignment
a variable of <? extends MyClass> type can only refer objects of types that inherit MyClass
this sets an upper bound
you can't write to it because
the compiler can't ensure that what you write is a derivate of its current content
and therefore it cannot enforce homogeneity (?!)
you can read <? super MyClass>'s from it
this variable is a producer
a variable of <? super MyClass> type can only refer objects of types that are inherited by MyClass
this sets a lower bound
you can write <? extends MyClass>'s to it
you can only read in Object's from it because the compiler only knows that it is a derivate of Object
this variable is a consumer
variables of <?> type can refer objects of any type
you can not write to it because
the compiler can't ensure that what you write is a derivate of its current content
and therefore it cannot enforce homogeneity (?!)
you can only read in Object's from it because the compiler only knows that it is a derivate of Object
these constraints are verified at compile time
class Wild {
public static void main(String args[]) {
/*
all errors are compile time
as long as the type of right hand side in the variable declaration
is compatible with the variable type,
its type does not influence future compatibilities of the variable
because its type in the assigned variable is not known to the compiler at the compile time
of course, anything can be done with explicit type casts
*/
MyGen<? extends MyClass> ge = new MyGen<MyClass>(new MyClass(1, 2));
// ge = MyGen, extends
// ge.e cannot be written but it can be read
// ge.e cannot be written because it may hold any class equal to or below MyClass
// and they need to ensure ge.e's homogeneity (?!)
// ge.e is a producer
// ge.e = new MyBase(3);
// ByBase cannot be converted to CAP#1
// CAP#1 extends MyClass from capture of ? extends MyClass
// ge.e = new MyClass(4, 5);
// MyClass cannot be converted to CAP#1
// CAP#1 extends MyClass from capture of ? extends MyClass
// ge.e = new MyDeriv(6, 7, 8);
// MyDeriv cannot be converted to CAP#1
// CAP#1 extends MyClass from capture of ? extends MyClass
MyBase geb = ge.e;
MyClass gec = ge.e;
// MyDeriv ged = ge.e;
// incompatible types, ge.e can have any type equal or below MyClass
System.out.println("ge.e = " + ge.e + "\t\tge.e.i = " + ge.e.i + "\tge.e.j = " + ge.e.j);
MyGen<? super MyClass> gs = new MyGen<MyClass>(new MyClass(1, 2));
// gs = MyGen, super
// gs.e can be written but it can be read only into an Object
// gs.e can be read only into an Object because it may hold any class equal or above MyClass
// gs.e is a consumer
// gs.e = new MyBase(3);
// incompatible types, gs.e can have any type equal to or above MyClass
gs.e = new MyClass(4, 5);
gs.e = new MyDeriv(6, 7, 8);
// MyBase gsb = gs.e;
// CAP#1 cannot be converted to MyBase
// CAP#1 extends Object super: MyClass from capture of ? super MyClass
// MyClass gsc = gs.e;
// CAP#1 cannot be converted to MyClass
// CAP#1 extends Object super: MyClass from capture of ? super MyClass
// MyDeriv gsd = gs.e;
// CAP#1 cannot be converted to MyDeriv
// CAP#1 extends Object super: MyClass from capture of ? super MyClass
Object gso = gs.e;
// the type of gs.e is known at run time
System.out.println("gs.e class is: " + gs.e.getClass().getName());
// gs.e class is: MyDeriv
// but not at compile time, when gs.e is only known to be of type Object
// System.out.println("gs.e = " + gs.e + "\t\tgs.e.i = " + gs.e.i + "\tgs.e.j = " + gs.e.j +
// "\tgs.e.k = " + gs.e.k);
// cannot find symbols gs.e.i, gs.e.j and gs.e.k
MyGen<?> g = new MyGen<MyClass>(new MyClass(1, 2));
// g = MyGen
// we know nothing about the type of g
// so the only allowed operation is to read it into an Object
// g.e = new MyBase(3);
// MyBase cannot be converted to CAP#1
// CAP#1 extends Object from capture of ?
// g.e = new MyClass(4, 5);
// MyClass cannot be converted to CAP#1
// CAP#1 extends Object from capture of ?
// g.e = new MyDeriv(6, 7, 8);
// MyDeriv cannot be converted to CAP#1
// CAP#1 extends Object from capture of ?
// MyBase gb = g.e;
// CAP#1 cannot be converted to MyBase
// CAP#1 extends Object from capture of ?
// MyClass gc = g.e;
// CAP#1 cannot be converted to MyClass
// CAP#1 extends Object from capture of ?
// MyDeriv gd = g.e;
// CAP#1 cannot be converted to MyDeriv
// CAP#1 extends Object from capture of ?
Object go = g.e;
// System.out.println("g.e = " + g.e + "\t\tg.e.i = " + g.e.i + "\tg.e.j = " + g.e.j);
// cannot find symbols g.e.i and g.e.j
}
}
// my generic class: it stores an object of a generic type
class MyGen<T> {
T e;
// element
MyGen(T e) {
this.e = e;
}
}
class MyBase {
int i;
MyBase(int i) {
this.i = i;
}
}
class MyClass extends MyBase {
int j;
MyClass(int i, int j) {
super(i);
this.j = j;
}
}
class MyDeriv extends MyClass {
int k;
MyDeriv(int i, int j, int k) {
super(i, j);
this.k = k;
}
}