Skip to content

Instantly share code, notes, and snippets.

@ThierryAbalea
Last active December 5, 2019 22:25
Show Gist options
  • Save ThierryAbalea/af2d85e8e1e02bfa1d48097eb1e71aca to your computer and use it in GitHub Desktop.
Save ThierryAbalea/af2d85e8e1e02bfa1d48097eb1e71aca to your computer and use it in GitHub Desktop.
public class Generics1 {
static class GenType<T> {}
static class TypeA {}
static class TypeB extends TypeA {}
static class TypeC extends TypeB {}
static <T> void method(GenType<T> arg1, T arg2) {}
public static void main(String[] args) {
GenType<TypeA> arg1A = new GenType<>();
GenType<TypeB> arg1B = new GenType<>();
GenType<TypeC> arg1C = new GenType<>();
TypeA arg2A = new TypeA();
TypeB arg2B = new TypeB();
TypeC arg2C = new TypeC();
// arg2 type must be the parametrized type or any of its sub-type
method(arg1A, arg2A); // stmt 1
method(arg1A, arg2B); // stmt 2
method(arg1A, arg2C); // stmt 3
method(arg1B, arg2A); // stmt 4 - compilation error
method(arg1B, arg2B); // stmt 5
method(arg1B, arg2C); // stmt 6
method(arg1C, arg2A); // stmt 7 - compilation error
method(arg1C, arg2B); // stmt 8 - compilation error
method(arg1C, arg2C); // stmt 9
}
}
public class Generics2 {
static class GenType<T> {
void method1(T arg) {}
T method2() { return null; }
}
static class TypeA {}
static class TypeB extends TypeA {}
static class TypeC extends TypeB {}
/**
* "? extends TypeB" corresponds to a Upper Bounded Wildcard.
* An upper bounded wildcard restricts the unknown type to be a specific type or a subtype of that type.
* The upper bound here is TypeB.
* It means than arg can be GenType<TypeB> or GenType<TypeC> (TypeC is a subtype of TypeB)
* "? extends LowerBoundType" is useful in a given context the object may be considered as a producer/source.
* We get objects from it, we don't put objects.
* We can say than the argument is covariant.
* Joshua Bloch, in the book "Effective Java", has introduced the mnemonic: PECS (Producer Extends, Consumer Super).
*/
static void method(GenType<? extends TypeB> arg) {
// any call to a method taking in parameter an object
// whose type corresponds to the parameterized type is forbidden.
// there is an exception: the null reference.
// See the comment regarding "stmt 5", to a probable explanation
// of why not even a single call is allowed.
arg.method1(null);
arg.method1(new Object()); // compilation error
arg.method1(new TypeA()); // stmt 1 - compilation error
arg.method1(new TypeB()); // stmt 2 - compilation error
// It can be surprising that the following statement is not allowed.
// arg can be only: GenType<TypeB> or GenType<TypeC>
// It seems OK to pass (to add in case GenType is a list) an instance of TypeC.
// My best guess is to avoid in the future compilation errors on
// existing/working code if we add a new sub type (e.g. class TypeD extends TypeC)
// Indeed, in this case, the following statement must stop to compile.
arg.method1(new TypeC()); // stmt 3 - compilation error
// the return object may have TypeB or any of its super types
Object object = arg.method2();
TypeA typeA = arg.method2(); // stmt 4
TypeB typeB = arg.method2(); // stmt 5
TypeC typeC = arg.method2(); // stmt 6 - compilation error
}
public static void main(String[] args) {
// the parametrized type must be TypeB or any of its sub types.
// It is not the case for Object & TypeA.
method(new GenType<Object>()); // compilation error
method(new GenType<TypeA>()); // stmt 7 - compilation error
method(new GenType<TypeB>()); // stmt 8
method(new GenType<TypeC>()); // stmt 9
}
}
/**
* Same code than Generics2, only the keyword "extends"
* have been replaced by "super"
*/
public class Generics3 {
static class GenType<T> {
void method1(T arg) {}
T method2() { return null; }
}
static class TypeA {}
static class TypeB extends TypeA {}
static class TypeC extends TypeB {}
/**
* "? super TypeB" corresponds to a Lower Bounded Wildcard.
* A lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type.
* The lower bound here is TypeB.
* It means than arg can be GenType<TypeB>, GenType<TypeA> or GenType<Object> (TypeA & Object are super types of TypeB)
* "? super LowerBoundType" is useful in a given context the object may be considered as a consumer/sink.
* We don't get objects from it, we only put objects.
* A return object can exists but with a different type than the parameter type.
* We can say than the argument is contravariant.
* Joshua Bloch, in the book "Effective Java", has introduced the mnemonic: PECS (Producer Extends, Consumer Super).
*/
static void method(GenType<? super TypeB> arg) {
// because TypeB is the more specific type in the allowed
// parametrized types (TypeB, TypeA & Object), we are only
// allow to pass TypeB or any of its sub types, like TypeC.
// Indeed, GenType<TypeB> may access a class member
// (instance variables or methods) of TypeB than TypeA have not.
arg.method1(null);
arg.method1(new Object()); // compilation error
arg.method1(new TypeA()); // stmt 1 - compilation error
arg.method1(new TypeB()); // stmt 2
arg.method1(new TypeC()); // stmt 3
// because arg can be GenType<Object>, the return object
// may have the type Object or any of its sub types (including
// sub types not in the hierarchy of TypeA, like String)
Object object = arg.method2();
TypeA typeA = arg.method2(); // stmt 4 - compilation error
TypeB typeB = arg.method2(); // stmt 5 - compilation error
TypeC typeC = arg.method2(); // stmt 6 - compilation error
}
public static void main(String[] args) {
// the parametrized type must be a TypeB or any of its super types.
// It is not the case for TypeC.
method(new GenType<Object>());
method(new GenType<TypeA>()); // stmt 7
method(new GenType<TypeB>()); // stmt 8
method(new GenType<TypeC>()); // stmt 9 - compilation error
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment