* Added the diamond operator (<>) * Update guide/english/java/generics/index.md Co-Authored-By: KartikSoneji <kartiksoneji@rocketmail.com> * Changed <> to < and > * Changed < and > to be escaped by `. Fixed Code Style to be Consistent. * Formatting changes
5.2 KiB
title
title |
---|
Generics |
Generics
Java Generics is a way to conviently use collections and classes for specific data types with out having to cast data back into the original data type. This prevents a lot of headache in the form of compile time and run time bugs.
Simply put, Generics lets you explicitly say that, for example, an ArrayList
object holds Integers
so that when you call the get()
method, you do not need to convert between Object
and Integer
. Below is an example of how you would have used an ArrayList
prior to Generics.
import java.util.ArrayList;
public class Example {
private ArrayList classNames;
public Example() {
classNames = new ArrayList();
}
public void addName(String name) {
classNames.add(name);
}
public String getNameAtIndex(int index) {
return (String) classNames.get(index);
}
}
The main problem with the above is if somehow an Object
not of type String
got added to the ArrayList
then the getNameAtIndex(int index)
method would result in a runtime error. To solve this we use Generics.
The syntax for Generics is very simple. Below is an example of instantiating an ArrayList.
import java.util.ArrayList;
public class Example {
private ArrayList<String> classNames;
public Example() {
classNames = new ArrayList<String>();
}
public void addName(String name) {
classNames.add(name);
}
public String getNameAtIndex(int index) {
return classNames.get(index);
}
}
The syntax to create your own Generic class would be as follows.
import java.util.ArrayList;
public class Example <T> {
private ArrayList<T> classNames;
public Example() {
classNames = new ArrayList<T>();
}
public void addName(T name) {
classNames.add(name);
}
public T getNameAtIndex(int index) {
return classNames.get(index);
}
}
Note that inside the angular brackets when naming the class, you can ensure that the Generic type is something that
you want. For example, if you wanted to make sure the type can be read as a form of String, you would go <T extends String>
.
Note that the letter T
is a placeholder, you could make that anything you like, as long as you use the same one
throughout the class.
The syntax to create your own Generic methods would be as follows.
public class GenericMethod {
public static <T> void printObject(T t) {
System.out.println(t.toString());
}
}
Type Erasure in Java Generics
One of the downsides to introducing generics in Java 1.5 was ensuring backward compatibility, to make sure existing programs continue to work and all existing libraries would still be able to use generic types.
Erasure literally means that the type information which is present in the source code is erased from the compiled bytecode.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericsErasure {
public static void main(String args[]) {
List<String> list = new ArrayList<String>();
list.add("Hello");
Iterator<String> iter = list.iterator();
while(iter.hasNext()) {
String s = iter.next();
System.out.println(s);
}
}
}
If you compile this code and then decompile it with a Java decompiler, you will get something like this.
import java.io.PrintStream;
import java.util.*;
public class GenericsErasure {
public GenericsErasure() {}
public static void main(String args[]) {
List list = new ArrayList();
list.add("Hello");
String s;
for(Iterator iter = list.iterator(); iter.hasNext(); System.out.println(s))
s = (String)iter.next();
}
}
Notice that the decompiled code contains no trace of the type information present in the original source code.
When you compile some code against a generic type or method, the compiler works out what you really mean (i.e. what the type argument for T
is)
and verifies at compile time that you're doing the right thing, but the emitted code again just talks in terms of java.lang.Object
- the compiler generates extra casts where necessary.
At execution time, a List<String>
and a List<Date>
are exactly the same - the extra type information has been erased by the compiler.
The Diamond Operator (<>)
Type variables need to be typed twice: once while declaring the data type, and once while calling the constructor. This can get quite unsightly, and a better way was introduced in Java SE 7 - the diamond operator (<>). The diamond operator replaces the repetiton in type variables while calling the constructor by replacing them with <>
, and the compiler infers the type from the type of the variable the object is being assigned to.
For example
ArrayList<Integer> a = new ArrayList<Integer>();
HashMap<Integer, String> hm = new HashMap<Integer, String>();
Can be replaced with
ArrayList<Integer> a = new ArrayList<>();
HashMap<Integer, String> hm = new HashMap<>();
Or even further, with abstract classes
List<Integer> a = new ArrayList<>();
Map<Integer, String> hm = new HashMap<>();