1. What is generics?
Generics (Generic type or generics) are an extension of the Java language type system to support the creation of classes that can be parameterized by type.
You can see the motivations for generics in the collection Framework (Collection framework). For example, the map class allows you to add an object of any class to a map, even if the most common scenario is to save an object of a particular type (such as a String) in a given map (map).
Because Map.get () is defined to return an Object, it is generally necessary to cast the result of Map.get () to the type you expect, such as:
Map m = new HashMap ();
M.put ("Key", "Blarg");
string s = (string) m.get ("key");
But it is possible that someone has saved something that is not a String in the map, so the above code will throw classcastexception.
Ideally, you might come to the view that M is a map that maps a string key to a string value. This allows you to eliminate coercion of type conversions in your code and to obtain an additional type-checking layer that prevents someone from saving the key or value of the wrong type in the collection. This is the work that generics do.
2. Benefits of Generics
The introduction of generics in the Java language is a large feature enhancement. Not only has the language, type system, and compiler changed significantly to support generics, but class libraries have also been overhauled, so many important classes, such as the collection framework, have become generics. This brings a number of benefits:
Type safety. The primary goal of generics is to improve the type-safety of Java programs. By knowing the type limits of variables defined using generics, the compiler can validate the type hypothesis at a much higher level. Without generics, these assumptions exist only in the programmer's mind (or, if you're lucky, in code comments).
A popular technique in Java programs is to define a collection in which its elements or keys are public types, such as "string List" or "string-to-string mapping". By capturing this additional type information in a variable declaration, generics allow the compiler to enforce these additional type constraints. Type errors can now be captured at compile time, rather than being shown as classcastexception at run time. Moving type checking from runtime to compile can help you find errors more easily and improve the reliability of your program.
Eliminates forced type conversions. A side benefit of generics is the elimination of many coercion type conversions in the source code. This makes the code more readable and reduces the chance of error.
Although reducing coercion type conversions can reduce the amount of verbose code that uses generic classes, declaring generic variables can lead to a corresponding wordy. Compare the following two code examples.
The code does not use generics:
List li = new ArrayList ();
Li.put (New Integer (3));
Integer i = (integer) li.get (0);
The code uses generics:
list<integer> li = new arraylist<integer> ();
Li.put (New Integer (3));
Integer i = li.get (0);
Using a generic variable once in a simple program does not reduce the amount of wordy. However, for large programs that use generic variables more than once, you can accumulate them to reduce the amount of wordy.
Potential performance gains. Generics are possible for larger optimizations. In the initial implementation of generics, the compiler enforces type conversions (without generics, programmers specify these coercion type conversions) into the generated bytecode. But the fact that more type information is available for compilers is possible for future versions of JVM optimizations.
Because of the way generics are implemented, generics are supported (almost) without the need for JVM or class file changes. All work is done in the compiler, and the compiler generates code that is similar to what is written when there is no generics, but is more secure type-safe.
3. Examples of generic usage
The following code example shows part of the definition of the Map interface in the collection framework in JDK 5.0:
Public interface Map<k, v> {
public void put (K key, V value);
Public V get (K key);
}
Note the two additional objects of the interface:
Type parameters K and V are at the class-level specification, representing placeholders for the type specified when declaring a variable of type Map.
The K and V are used in the method signatures of Get (), put (), and other methods.
To gain the benefits of using generics, you must provide specific values for K and V when you define or instantiate a variable of type Map. Do this in a relatively straightforward way:
Map<string, string> m = new hashmap<string, string> ();
M.put ("Key", "Blarg");
String s = m.get ("key");
When using a generic version of Map, you no longer need to cast the result of Map.get () to string, because the compiler knows that get () will return a string.
There is no reduction in keyboard entry in versions that use generics; in fact, more typing is needed than a version that uses forced type conversions. The use of generics simply brings additional type-safety. Because the compiler knows more about the types of keys and values you will put into a Map, type checking moves from execution to compile time, which increases reliability and accelerates development.
4. Named type parameters
The recommended naming convention is to use an uppercase single letter name as the type parameter. This differs from the C + + conventions (see Appendix A: Comparisons to C + + templates) and reflects the assumption that most generic classes will have a small number of type parameters. For common generic patterns, the recommended name is:
The k--key, such as the mapped key.
v--values, such as the contents of list and Set, or the values in a Map.
e--Exception class.
t--generic type.
5. Generics are not covariant
A common source of confusion about generics is to assume that they are covariant like arrays. In fact, they are not co-variable. List<object> is not a parent type of list<string>.
If a expands B, then the array of a is also an array of B, and can be used completely where b[] is needed a[]:
integer[] Intarray = new INTEGER[10];
number[] Numberarray = Intarray;
The above code is valid because an integer is a number, and an integer array is an array of number. But not for generics. The following code is not valid:
list<integer> intlist = new arraylist<integer> ();
list<number> numberlist = intlist; Invalid
This makes a lot of people feel trouble, but avoids the following problems:
person[] person = new STUDENT[5];
Person[0] = new Emploee ();
6. Type wildcard characters
Suppose you have this method:
void Printlist (List l) {
for (Object o:l)
System.out.println (o);
}
The above code is compiled on JDK 5.0, but if you try to invoke it with list<integer>, you get a warning. The warning occurs because you pass generics (list<integer>) to a method that only promises to treat it as a List (the so-called primitive type), which destroys type-safety using generics.
What if you try to write a method like this?
void Printlist (list<object> l) {
for (Object o:l)
System.out.println (o);
}
It still does not compile because a list<integer> is not a list<object> (as the previous screen generics are not learned in covariant). This is really annoying--now your generic version is no more useful than a normal non-generic version!
The solution is to use a type wildcard character:
void Printlist (list<?> l) {
for (Object o:l)
System.out.println (o);
}
The question mark in the code above is a type wildcard character. It is read as a "question mark". List<?> is the parent type of any generic List, so you can completely add list<object>, list<integer>, or list<list<list<flutzpah> >> passed to Printlist ().
Generic classes and generic methods in Java