The Java Tutorial: Generics
In any nontrivial software project, bugs are simply a fact of life. Careful planning, programming, and testing can help reduce their pervasiveness, but somehow, somewhere, they’ll always find a way to creep into your code. This becomes especially apparent as new features are introduced and your code base grows in size and complexity.
Fortunately, some bugs are easier to detect than others. Compile-time bugs, for example, can be detected early on; you can use the compiler’s error messages to figure out what the problem is and fix it right then and there. Runtime bugs, however, can be much more problematic; they don’t always surface immediately, and when they do, it may be at a point in the program that is far removed from the actual cause of the problem. Generics, introduced in Java SE 5.0, add stability to your code by making more of your bugs detectable at compile time.
Why Use Generics?
In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces, and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to reuse the same code with different inputs. The difference is that the inputs to formal parameters are values, while the inputs to type parameters are types.
Code that uses generics has many benefits over nongeneric code:
- Stronger type checks at compile time. A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.
- Elimination of casts. The following nongeneric code snippet requires casting:
List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0);
When rewritten using generics, the code does not require casting:
List<String> list = new ArrayList<String>(); list.add("hello"); String s = list.get(0); // no cast
- The ability to implement generic algorithms. By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.