Generic Classes in Java
Generic programming is a paradigm that produces highly general and reusable code, meaning code not tied to one narrow situation.
From Using Generic Classes to Writing Them
So far you have worked with generic classes and generic methods that already exist. The Java Collection Framework supplies many of them, and you have been using them as finished tools. This material begins a shift in perspective. Instead of only consuming generic code, you are now going to look at why someone would want to write new generic classes and methods from scratch, and what that effort buys.
The central question is simple: what is the payoff? A useful way to hold that question in mind is to ask, as you read, what a piece of code would have to look like if it needed to keep working across many different situations without being rewritten each time.
Generic programming is a paradigm that produces highly general and reusable code. General code is code that is not tied to one narrow situation. Reusable code is code that can be used again in many settings. Keep those two properties in mind, because the rest of this article explains why they matter and who gains from them.
One Class, Many Concrete Types
Consider the alternative first. Without a general approach, a programmer solving a similar problem for several different situations may end up writing several nearly identical pieces of code, each one locked to its own situation. Generic programming pushes in the opposite direction, toward a single piece of code that serves many situations.
A generic class handles this by introducing a type parameter, written inside angle brackets after the class name. In the class Box<T>, the letter T is a placeholder. It does not name a real type yet. A real type is supplied later, at the point where the class is used, so the same class definition can become Box<Integer> in one place and Box<String> in another.
Read the diagram from the top down. The class definition, Box<T>, is written once. Each use fills the placeholder with a concrete type. What changes between the two after-states is only the type that T stands for. What stays the same is the structure of the class: the same fields, the same methods, and the same logic. That shared structure is what makes the code general.
A Small Generic Box
The following example is an original illustration written for this article. It defines one generic class and uses it twice, once with Integer and once with String. Read it with the diagram above in mind: the class body is written once, and the main method supplies the concrete types.
43
5- Box<T> declares the type parameter T after the class name
- private T value uses T as the type of the field
set(T value)and Tget()use T in method signatures- Box<Integer> and Box<String> fill T with two different concrete types
What do you think happens?
Before reading on, what does the program print on its first line, and why?
Reveal answer
43, because get() returns an Integer and 1 is added to it
intBox was parameterized with Integer, so intBox.get() returns an Integer value of 42, and adding 1 produces 43. The same class body, with T filled in as String, served textBox, which is why the second line prints 5, the length of hello.
Tracing a Generic Box Through Its Methods
Following the Type Parameter Through One Use
Trace what happens to the type parameter T when the code creates a Box<String> and stores a value in it.
- 1Declare the use
Box<String>
textBoxfills the placeholder T with String. Inside this use of the class, every place where T appears now stands for String. - 2Create the object
new Box<>() builds a box. The field declared as private T value now holds a slot whose type is String for this object.
- 3Call set
textBox.set("hello")passes a String, which matches the parameter type T as filled in for this object. The field value now refers to hello. - 4Call get
textBox.get()returns the stored value with the type String, so callinglength()on it is valid and returns 5.
The same class body handled the String case without any change to its code. Only the type filled into T differed from the Integer case.
Notice what this trace did not require. You did not write a separate StringBox class with its own set and get methods. The generic class carried the logic once, and each use supplied the type that the logic should work with. This is the practical meaning of highly general code: one definition, many situations.
Reading the Type Parameter in a Signature
Generic syntax is what you will meet when you read Javadoc. Javadoc is the documentation that describes Java's standard classes, and the standard generic classes are part of that material. In a signature such as Map<K,V>, the angle brackets contain the type parameters. Each name inside them is a placeholder that a user of the class will later replace with a real type.
Reading a signature is a matter of separating two roles. The name before the angle brackets identifies the generic type. The names inside the brackets are its type parameters, and each one stands for a type that is chosen when the generic type is used. When you see a signature like this in documentation, the question to ask is which concrete types the user must supply in those slots.
Who Benefits and Why
The source identifies a specific group that benefits most directly from generic programming: people who write reusable software libraries. A software library is a collection of code that other programmers can call on, and its value depends on how many situations it can serve. A library author who writes general, reusable code gives every future user of that library a broader set of options.
The source makes a careful distinction here. Not every programmer needs to write reusable software libraries, but every programmer should know at least a little about how to do it. The motivation is not that everyone must become a library author. The point is that a basic understanding of the idea is valuable even to programmers who will never publish a library.
There is also a practical reason that reaches everyone. Reading the Javadoc documentation for Java's standard generic classes requires knowing some of the generic syntax introduced in this material. So even a programmer who only uses library classes gains from understanding how type parameters are written and what they mean.
Mistakes When Reading Generic Code
Common mistake Treating T as a real class named T
What it looks likeLooking for a file or class called T when reading Box<T>
Why it failsT is a placeholder. It is filled in with a concrete type at the point where the class is used.
Do this insteadRead the name inside the angle brackets as a slot that a user will fill, and ask what type goes there in each use.
Common mistake Assuming Box<Integer> and Box<String> need separate class definitions
What it looks likeWriting IntBox and StringBox with duplicate set and get methods
Why it failsThe generic class body is written once and serves both uses, which is the source of its generality.
Do this insteadKeep one generic definition and let each use supply its type.
Common mistake Skipping the generic syntax when reading Javadoc
What it looks likeIgnoring the <K,V> part of Map<K,V> in the documentation
Why it failsThe source notes that reading the Javadoc for standard generic classes requires knowing some of this syntax.
Do this insteadIdentify the type parameter list first, then match each name to the role it plays in the class or method.
Practice: Tracing a Type Parameter
Using the Box<T> class from the example, explain in your own words what the single class definition contributes and what each use contributes. Then state what type the compiler knows textBox.get() returns, and why that knowledge matters when you call a method on it.
Hints
- Separate the class body, which is written once, from the concrete type supplied at each use.
- Look at which methods mention T and what they promise to return or accept.
- Think about what would go wrong if
get()returned an unknown type instead.
Read the signature Map<K,V> and write one sentence that names the generic type, one that names its type parameters, and one that says which concrete types a user must choose when using it.
Hints
- The generic type name appears before the angle brackets.
- The type parameters are the names inside the angle brackets.
Key Takeaways
- Generic programming is a paradigm that produces highly general and reusable code, meaning code not tied to one narrow situation.
- A generic class declares a type parameter, such as T in Box<T>, that is a placeholder filled in with a concrete type at each use, so one class body serves many types.
- Writers of reusable software libraries benefit most directly from generic programming, but every programmer should know at least a little about it.
- Reading Javadoc for Java's standard generic classes, such as Map<K,V>, requires recognizing the type parameter syntax.