// Assumption : Dog and Cat extends Animal // SENARIO 1 List<Animal> animalList = new ArrayList<Animal>(); // YES List<Animal> animalList = new ArrayList<Dog>(); // NO : Reason 1 // SENARIO 2 List<Animal> animalList = new ArrayList<Animal>(); animalList.add(new Dog()); //YES animalList.add(new Cat()); //YES // SENARIO 3 List<Dog> dogs = new ArrayList<Dog>(); dogs.add(new Dog()); sendBackHome(dogs); // YES sendBackHomez(dogs); // YES sendBackShop(dogs); // YES sendBackShopz(dogs); // NO : Reason 2 static void sendBackHome(List<Dog> animals){ ... } // Wildcard allow any type pass in as parameter but cannot add static void sendBackShop(List<? extends Animal> animals) { animals.add(new Dog()); //NO : Reason 2 } // Wildcard allow any type pass in as parameter but cannot add static void sendBackHomez(List<?> animals){ animals.add(new Dog()); //NO : Reason 2 } static void sendBackShopz(List<Animal> animals){ ... } static void sendBackShopz(List<Object> animals){ ... } // SENARIO 4 void sendBackHome(List<? extends Serializable> animals) {} //YES void sendBackHome(List<? implements Serializable> animals) {} //NO // SENARIO 5 List<?> animals = new ArrayList<Dog>(); // YES List<? extends Animal> animals = new ArrayList<Dog> // YES List<? super Dog> animals = new ArrayList<Animal> // YES List<? super Animal> animals = new ArrayList<Dog>; // NO List<Animal> animals = new ArrayList<? super Dog>; // NO //Wildcard notation cannot used in object creation // SENARIO 6 public class Zoo<T extends Animal> { public static void main(String[] args) { Zoo<Dog> zoo = new Zoo<Dog>(); // YES Zoo<Integer> zoo = new Zoo<Integer>(); // NO } } // SENARIO 7 public class Zoo<T extends Serializable> {} // YES public class Zoo<T implements Serializable> {} // NO // SENARIO 8 public class Zoo<T extends Animal> {} // YES public class Zoo<? extends Animal> {} // NO // SENARIO 9 public class Zoo { public <T extends Animal> Zoo(T t){} //Constructor public static void main(String[] args) { Zoo zoo = new Zoo(new Dog()); // YES Zoo<Dog> zoo = new Zoo<Dog>(new Dog()); // NO } } // SENARIO 10 public class Zoo { public <T> void addAnimal(T t) { List<T> animals = new ArrayList<T>(); // YES animals.add(t); } } public class Zoo { List<T> animals = new ArrayList<T>(); // NO public <T> void addAnimal(T t) { animals.add(t); } } /* -------------------------- APPENDIX -------------------------- Reason 1 -------- Generic type of reference and object must identical. Reason 2 -------- Due to type erasure, "animals" only recognize it's based type which is ArrayList ONLY during runtime, and no other generic type. Let's imagine senario below: When we write this: static void sendBackHome(List<Animal> animals){ animals.add(new Cat()); } When runtime, what jvm see is only base type, NOT generic type due to type erasure static void sendBackHome(List animals){ animals.add(new Cat()); // now it content Cat and Dog object } See the problem ? List<Animal> animals able to add Cat object. when we try to process, animals list, which object should we cast? */