A Collection can contain any object, for example either a String or Integer. One would feel this is more flexible but this would introduce a type safety problem to your application. Standard Collections can be restricted to store only certain type of object. So Generics was introduced to solve this problem.
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add(1);
- Above list can have only Strings
- In the line highlighted in red, you are getting a error at compile time.
- Generics stops run-time errors at compile time.
- Generics help us to have type safety.
Type Safety Violations
public class Container {
Object
object = null;
public void add(Object obj) {
object = obj;
}
public Object get() {
return object;
}
public void print() {
System.out.println("Container: ".concat((String)object));
}
public static void main(String[] args) {
Container
container = new Container();
container.add("test");
//Throws an error
due violation of Java Static Typing.
//The error is
thrown within the print() method.
container.add(123);
container.print();
}
}
The Class Cast exceptions is thrown at the run time.
How to Achieve Type Safety
In the below example you can achieve the Type Safety at a cost. That is you need to introduce separate containers for each type for example String and Object.
public class StringContainer {
String
object = null;
public void add(String obj) {
object = obj;
}
public String get() {
return object;
}
public void print() {
System.out.println("Container: ".concat(object));
}
public static void main(String[] args) {
StringContainer container = new StringContainer ();
container.add("test");
//Identifies the
Type Safety violation at compile time
container.add(123);
container.print();
}
}
Above code solves the Type Safety problem but you have to do copy and paste operations to create new classes for each type.
To solve the conflict between Type Safety and Boilerplate, you have to use Generics.
public class Container<T> {
T
object = null;
public void add(T obj) {
object = obj;
}
public T get() {
return object;
}
public void print() {
System.out.println("Container:
" +
object);
}
public static void main(String[] args) {
Container<String>
container = new
Container<String>();
container.add("test");
//Error: The method add(String) in the type
Container<String> is not //applicable for the arguments (int)
container.add(123);
container.print();
}
}
Generic Collections
Generics List with For Each Loop
import java.util.*;
public class MList {
//Diamond Operator <>.
//Diamond operator says don’t add the Generics in
explicitly, just
//infer the context
List<Person> personList = new ArrayList<>();
public MList() {
personList.add(new Person("Mahesh"));
personList.add(new Person("Earth"));
}
public List<Person>
getPerson() {
return personList;
}
public static void main(String[] args) {
for (Person person : new MList().getPerson())
{
System.out.println(person.getName());
}
}
}
Sets
import java.util.*;
public class MList {
Set<Person>
personList = new HashSet<>();
public MList() {
Person
mahesh = new Person("Mahesh");
Person
earth = new Person("Earth");
personList.add(mahesh);
personList.add(earth);
personList.add(mahesh);
}
public Set<Person>
getPerson() {
return personList;
}
public static void main(String[] args) {
for (Person person : new MList().getPerson())
{
System.out.println(person.getName());
}
}
}
Sets does not allow duplicates only unique elements.
Map
Example with two generic parameters
import java.util.*;
public class MList {
Map<String,
Person> personMap = new HashMap<>();
public MList() {
Person
mahesh = new Person("Mahesh");
Person
earth = new Person("Earth");
personMap.put("Mahesh", mahesh);
personMap.put("Earch", earth);
personMap.put("Mahesh", mahesh);
}
public Map<String,
Person> getPersonMap() {
return personMap;
}
public static void main(String[] args) {
for (Map.Entry<String, Person> entry : new
MList().getPersonMap().entrySet()) {
System.out.println(entry);
}
}
}
Generic Classes and Interfaces
Implementing a Generic Type
public class AgeComparator implements
Comparator<Person> {
public int compare(Person left, Person right) {
return Integer.compare(left.getAge(), right.getAge());
}
public static void main(String[] args) {
Person
mahesh = new Person("Mahesh", 34);
Person
earth = new Person("Earth", 25);
Person
down = new Person("Down", 28);
List<Person>
personList = new ArrayList<>();
personList.add(mahesh);
personList.add(earth);
personList.add(down);
for(Person person : personList) {
System.out.println(person.getAge());
}
Collections.sort(personList, new AgeComparator());
System.out.println("------------");
for(Person person : personList) {
System.out.println(person.getAge());
}
}
}
Passing a Parameter to a Generic Type
public class
ReverseComparator<T> implements Comparator<T> {
private final Comparator<T> delegateComparator;
public
ReverseComparator(Comparator<T> delegateComparator) {
this.delegateComparator = delegateComparator;
}
public int compare(T left, T right) {
return -1 * delegateComparator.compare(left, right);
}
public static void main(String[] args) {
}
}
public class AgeComparator implements
Comparator<Person> {
public int compare(Person left, Person right) {
return Integer.compare(left.getAge(), right.getAge());
}
public static void main(String[] args) {
Person
mahesh = new Person("Mahesh", 34);
Person
earth = new Person("Earth", 25);
Person
down = new Person("Down", 28);
List<Person>
personList = new ArrayList<>();
personList.add(mahesh);
personList.add(earth);
personList.add(down);
Collections.sort(personList, new
ReverseComparator<Person>(new AgeComparator()));
System.out.println("------------");
for(Person person : personList) {
System.out.println(person.getAge());
}
}
}
Type Bounds
public class SortedPair <T extends
Comparable<T>> {
private final T first;
private final T second;
public SortedPair(T left, T right) {
if(left.compareTo(right) > 0) {
first = left;
second = right;
}
else {
first = right;
second = left;
}
}
public T getFirst() {
return first;
}
public T getSecond() {
return second;
}
public static void main(String[] args) {
SortedPair<Integer>
pair1 = new
SortedPair<Integer>(21, 32);
System.out.println(pair1.getFirst());
System.out.println(pair1.getSecond());
SortedPair<Integer>
pair2 = new
SortedPair<Integer>(32, 21);
System.out.println(pair2.getFirst());
System.out.println(pair2.getSecond());
}
}
Generics on Methods
public class AgeComparator implements
Comparator<Person> {
public int compare(Person left, Person right) {
return Integer.compare(left.getAge(), right.getAge());
}
public static void main(String[] args) {
Person
mahesh = new Person("Mahesh", 34);
Person
earth = new Person("Earth", 25);
Person
down = new Person("Down", 28);
List<Person>
personList = new ArrayList<>();
personList.add(mahesh);
personList.add(earth);
personList.add(down);
Person
youngestMember =
(Person)
min(personList, new AgeComparator());
System.out.println(youngestMember.getAge());
youngestMember = minWithGenerics(personList, new AgeComparator());
System.out.println(youngestMember.getAge());
List<Integer>
numbers = new
ArrayList<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
//Method Reference
[Integer::compare]
System.out.println(minWithGenerics(numbers, Integer::compare));
}
public static Object min(List
values, Comparator comparator) {
Object
lowestElement = values.get(0);
for(int i = 1; i < values.size(); i++) {
final Object element = values.get(i);
if (comparator.compare(element, lowestElement) < 0) {
lowestElement = element;
}
}
return lowestElement;
}
public static <T> T
minWithGenerics(List<T> values, Comparator<T> comparator) {
T
lowestElement = values.get(0);
for(int i = 1; i < values.size(); i++) {
final T element = values.get(i);
if (comparator.compare(element, lowestElement) < 0) {
lowestElement = element;
}
}
return lowestElement;
}
}
Wildcards
The substitution principle
arrays are covariant that is arrays are not type safe. list are not covariant.Upper Bounded Wildcards
public class UpperBoundedWildcards { public static void main(String [] a) { Person manesh = new Person("Mahesh"); Person hadoop = new Person("Hadoop"); List<Person> personsList = new ArrayList<>(); personsList.add(manesh); personsList.add(hadoop); printPersonList(personsList); Engineer tom = new Engineer("Tom"); Engineer tim = new Engineer("Tim"); List<Engineer> engineerList = new ArrayList<>(); engineerList.add(tom); engineerList.add(tim); printPersonList_1(engineerList); } public static void printPersonList(final List<? extends Person> personList) { for(Person person : personList) { System.out.println(person.getName()); } } public static <T extends Person> void printPersonList_1(final List<T> personList) { for(Person person : personList) { System.out.println(person.getName()); } } } class Person { private String name; public Person(String name ) { this.name = name; } public String getName() { return name; } } class Engineer extends Person { public Engineer(String name) { super(name); } }
Lower Bounded Wildcards
public class LowerBoundedWildcards { public static void main(String [] a) { Person manesh = new Person("Mahesh"); Person hadoop = new Person("Hadoop"); List<Person> personsList = new ArrayList<>(); personsList.add(manesh); personsList.add(hadoop); AddToList(personsList); Engineer tom = new Engineer("Tom"); Engineer tim = new Engineer("Tim"); List<Engineer> engineerList = new ArrayList<>(); engineerList.add(tom); engineerList.add(tim); AddToList(engineerList); } public static void AddToList(final List<? super Engineer> list) { list.add(new Engineer("Test")); } }
* SUPER for Adding and EXTENDS for iterating (getting)
? extends Vs. ? super
? extends? super
- Declare an upper bound for the type parameter.
- To get data out of the parameter
- Covariance
- Declare an lower bound for the type parameter
- To put data into the parameter
- Contravariance
Unbounded Wildcards
public class UnboundedWildcards { public static void main(String arg[]) throws ClassNotFoundException{ final Class<?> pluginClass = Class.forName("com.PluginClassName"); } }
Raw Types and Compatibility
N/A
Erasure
Reflections
N/A
Advanced Topics
N/A