What is generics?
Java
Intro
Generics is often used to write type-safe collections. This means that compiler stops you from putting a Dog into a list of Ducks. All potential exceptions that may occur at runtime can be caught at compile time by restricting the instance type stored in the collection class (via diamond operator).
Btw, generic means it is specified by user OUTSIDE the class, not inside the class.
Without generics, the compiler could not care less what you put into a collection, because all collection implementations hold type Object. You could put anything like String, Integer, Duck, Pumpkin in any ArrayList without generics; it’s like the ArrayList is declared as ArrayList
Without generics, the compiler would happily let you put a Pumpkin into an ArrayList that was supposed to hold only Cat objects.
With generics, you can create type-safe collections where more problems are caught at compile-time instead of runtime.
So all possible exceptions that may occur at runtime are caught at compile time by restricting instance types stored in the collection class.
3 things about generics
1) Creating instances of generic class (like ArrayList)
When you make ArrayList, you have to declare the type of objects to be allowed in the
list like new ArrayList
new ArrayList<Song>();
2) Declare and assign variables of generic types
How does polymorphism really work with generic types? If you have an
ArrayList
List<Song> songList = new ArrayList<Song>();
3) Declare and invoke methods that take generic types
It is similar to point 2. If you have a method that has as a parameter, say, an ArrayList of Animal objects, what does that really mean? Can you also pass it an ArrayList of Dog objects? We’ll look at some subtle and tricky polymorphism issues that are very different from the way you write methods that take plain old arrays.
void foo(List<Song> list);
x.foo(songList);
How to use generic class (like ArrayList)?
This is point 1 out of 3. The 2 areas in a generic class that you need to look are 1) class declaration 2) method declaration
“E” is the type used to create instance of ArrayList. You can think it as like E = Type.
Think of “E” as a stand-in for “the type of element you want this collection to hold and return.” (E is for Element.)
So here, ArrayList
E is replaced by the real type, which is known as type parameter that you use
when creating ArrayList instance. That is why add() method for ArrayList won’t let you add anything except objects of a
reference type that’s compatible with the type of “E.” So if you make an
ArrayList
E or T, anything that is a legal Java identifier.
How to use generic methods?
This is point 3 out of 3.
A generic method means that the method declaration uses a type parameter in its signature. We can use type parameters in several ways:
1) Use type parameter defined in class declaration
public class ArrayList<E> extends AbstractList<E> ... {
public boolean add (E o)
}
When you declare a type parameter for the class, you can simply use that type any place that you’d use a real class or interface type. The type declared in the method argument is essentially replaced with the type you use when you instantiate the class.
2) Use type parameter not defined in class declaration
//here we can say ArrayList<T> bcos we delcared T at the start of method declaration
public <T extends Animal> void takeThing(ArrayList<T list>)
If the class itself doesn’t use a type parameter, you can still specify one for a method, by declaring it in a really unusual (but available) space— before the return type. This method says that T can be “any type of Animal.
Btw:
public <T extends Animal> void takeThing(ArrayList<T list>)
//is not the same as
public void takeThing(ArrayList<Animal> list)
The first one, where
But the one on the bottom, where the method argument is
(ArrayList
This violates polymorphism bcos we want arraylists containing subclasses of Animal as method parameters too!
Generics in Polymorphism (in type-safe Collection objects)
Generics with polymorphism is counter-intuitive. Let’s try polymorphism with a generic type(class inside the <>)
But will List of animal parameter accept List of dog? NO!
List<Dog> dogs = List.of(new Dog(), new Dog());
takeAnimals(dogs);
//wont compile!
The whole point of polymorphism is that anything an Animal can do, a Dog can do that as well. But what if:
public void takeAnimals(List<Animal> animals){
animals.add(new Cat());
}
In this method, we added a Cat in a Dogs-ONLY list. So compiler will not let you take this risk.
If you declare a method to take List
, it can take ONLY a List , not List or List .
Generics in Polymorphism (in not type-safe arrays)
However, for not type-safe things like arrays, we can do:
Parent[] myParentArray = new Child[10];
public void addAnimals(Animal[] animals ) {
animals [0] = new Animal();
// If passed animal[] is of type Dog[] then we are adding a Cat object to a Dog[] array.
animals [1] = new Cat();
// If passed animal[] is of type Cat[] then we are adding a Dog object to a cat[] array.
animals [1] = new Dog();
}
public class callerClass() {
Animal[] animalArray = new Animal[10];
Cat[] catArray = new Cat[10];
Dog[] dogArray = new Dog[10];
addAnimals(animalArray); //Expected, no questions raised here.
addAnimals(catArray); //As Cat[] is a type of Animal[] so we may end up in adding a Cat in Dog Array.
addAnimals(dogArray); // As Dog[] is a type of Animal[] so if Cat[] is passed we may end up in adding a Dog in a //Cat array.
}
Again, this won’t work for type-safe collections like ArrayList.
Wildcard as solution to polymorphism
<K extends T> // T와 T의 자손 타입만 가능 (K는 들어오는 타입으로 지정 됨)
<K super T> // T와 T의 부모(조상) 타입만 가능 (K는 들어오는 타입으로 지정 됨)
<? extends T> // T와 T의 자손 타입만 가능
<? super T> // T와 T의 부모(조상) 타입만 가능
<?> // 모든 타입 가능. <? extends Object>랑 같은 의미
3 types of Wildcards are:
// Unbound
List<?> unboundList = new ArrayList<>();
// Upper bound
List<? extends Foo> upperList = new ArrayList<>();
// Lower bound
List<? super Bar> lowerList = new ArrayList<>();
WildCard wildCard = new WildCard();
wildCard.test(upperList);
wildCard.test(lowerList); // Error
// Bar의 super class중에 Foo가 있다고 해도 파라미터로 넘길 수 없다
I mentioned a generic method taking a type parameter.
It is similar to that, but wildcard is a bit different.
//remember the keyword "extends" means either extends OR implements
public void takeAnimals(List<? extends Animal> animlas){
for(Animal a: animals){
a.eat();
}
}
When you use a wildcard in your method argument, the compiler will STOP you from doing anything that could hurt the list referenced by the method parameter.
You can still call methods on the elements in the list, but you cannot add elements to the list!
Use method’s generic type parameter
public <T extends Animal> void takeAnimals(List<T> list){
}
This method is not being utilised to its true capability yet. But what if we changed this method to return a list, of all animals we have vaccinated? We can declare that the list that is returned has the same generic typ as the list passed as parameter.
public <T extends Animal> List<T> void takeAnimals(List<T> list){
}
When we call this method, we get the same type back as the type that we put inside.
List<Dog> dogs = List.of(new Dog(), new Dog());
List<Dog> vaccinatedDogs= takeAnimals(dogs);
List<Animal> animals = List.of(new Dog(), new Cat());
List<Animal> vaccinatedAnimals= takeAnimals(animals);
Wildcard for BOTH method parameter and return type
There is no guarantee method parameter and return type is of the same type. Actually, anything calling this method has no idea what is inside that collection, other than knowing it is subclass of Animal.
public void go(){
List<Dog> dogs = List.of(new Dog(), new Dog());
List<? extends Animal> vaccinatedSomeStuff = takeAnimals(dogs);
}
public List<? extends Animal> takeAnimals(List<? extends Animal> animals){
}
Difference between wildcard(?) and type parameter(T)
Using the wildcard (“? extends”) is fine when you don’t care much about the generic type, you just want to allow allsubtypes of some type.
Using a type parameter (“T”) is more helpful when you want to do more with the type itself, for example in the method’s return like List
vaccinatedAnimals
Summary
A generic class means that the class declaration includes a type parameter.
A generic method means that the method declaration uses a type parameter in its signature
Reference
Head first Java https://st-lab.tistory.com/153