Last active
December 5, 2019 22:25
-
-
Save ThierryAbalea/c77de6b28f07e55226e6a4020a919272 to your computer and use it in GitHub Desktop.
Answers to Java Generic Quiz (quizz https://gist.github.com/ThierryAbalea/a48002823945305fbc3410380d879206)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 (https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html). | |
* 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); // stmt 1 | |
arg.method1(new Object()); // stmt 2 - compilation error | |
arg.method1(new TypeA()); // stmt 3 - compilation error | |
arg.method1(new TypeB()); // stmt 4 - 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 5 - compilation error | |
// the return object may have TypeB or any of its super types | |
Object object = arg.method2(); // stmt 6 | |
TypeA typeA = arg.method2(); // stmt 7 | |
TypeB typeB = arg.method2(); // stmt 8 | |
TypeC typeC = arg.method2(); // stmt 9 - 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>()); // stmt 10 - compilation error | |
method(new GenType<TypeA>()); // stmt 11 - compilation error | |
method(new GenType<TypeB>()); // stmt 12 | |
method(new GenType<TypeC>()); // stmt 13 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Same code than Generics2, only the keyword "extends" | |
* have been replaced by "super" | |
*/ | |
public class Generics3 { | |
static class GenType<T> { | |
public List<T> list = new ArrayList<>(); | |
void method1(T arg) {list.add(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 (https://docs.oracle.com/javase/tutorial/java/generics/lowerBounded.html). | |
* 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); // stmt 1 | |
arg.method1(new Object()); // stmt 2 - compilation error | |
arg.method1(new TypeA()); // stmt 3 - compilation error | |
arg.method1(new TypeB()); // stmt 4 | |
arg.method1(new TypeC()); // stmt 5 | |
// 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(); // stmt 6 | |
TypeA typeA = arg.method2(); // stmt 7 - compilation error | |
TypeB typeB = arg.method2(); // stmt 8 - compilation error | |
TypeC typeC = arg.method2(); // stmt 9 - 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>()); // stmt 10 | |
method(new GenType<TypeA>()); // stmt 11 | |
method(new GenType<TypeB>()); // stmt 12 | |
method(new GenType<TypeC>()); // stmt 13 - compilation error | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment