Java Generics: Generic 1 - Use Generic Class
Java Generics: Generic 1 - Use Generic Class
Young
Java Generics
Handout written by Nick Parlante
In my opinion, Java generics are a mixed blessing. Some uses of generics are simple to understand and
make the code cleaner. They are a real improvement in the language, and these are the scenarios where we
will concentrate. There are other, more complex uses of generics that I find hard to read, and I'm not
convinced they are worth using at all.
The advantage is that the compiler knows that the add() and get() methods take and return Strings, and so
it can do the right type checking at compile time. We do not have to put in the (String) cast. The code
that uses the list is now more readable. As a benefit of compile time typing, Eclipse code-assist now
knows that strings.get(0) is a String and so can do code completion.
The type of the iterator -- e.g. Iterator<String> -- must match the type of the collection.
The plain type "List" without any generic type is known as the "raw" version. Raw versions still work in
Java, and they essentially just contain pointers of type Object. You can assign back and forth between
generic and raw versions, and it works, although it may give a warning.
- e.g. You can store a List<String> pointer in a variable just of type List. This works, but gives a
warning that it's better to keep it in List<String> the whole time.
At runtime, all the casts are checked in any case, so if the wrong sort of object gets into a List<String> it
will be noticed at runtime. This is why Java lets us mix between List<String> and raw List with just a
warning-- it's all being checked at runtime anyway as a last resort.
- e.g. String s = strings.get(0); // checks at runtime that the pointer coming out really is a String
Eclipse tip: with the cursor where the <String> would go, hit ctrl-space. If Eclipse can deduce from
context that a <String> or whatever is required, it puts it in for you, yay!
Boxing/Unboxing
Normally, you cannot store an int or a boolean in an ArrayList, since it can only store pointers to objects.
You cannot create an ArrayList<int>, but you can create an ArrayList<Integer>.
With Java 5 "auto boxing", when the code needs an Integer but has an int, it automatically creates the
Integer on the fly, without requiring anything in the source code. Going the other direction, auto
2
unboxing, if the code has an Integer but needs an int, it automatically calls intValue() on the Integer to
get the int value.
This works for all the primitives -- int, char double, boolean,
This works especially well with generic collections, taking advantage of the fact that the collection type
shows that it needs Integer or Boolean or whatever.
Foreach Loop
Javas for-loop that iterates over any collection or array:
List<String> strings = ...
This is a shorthand for looping over the elements with an iterator that calls hasNext()/next() in the usual
way. The fancy iterator features -- such as remove() -- are not available. Nonetheless, this syntax is
very handy for the very common case of iterating over any sort of collection.
The foreach does not provide a way to change the collection. In the loop, a statement like "s =
something" just changes the local variable s, not that slot in the collection. However "s.doSomething()"
is effective, since the local s points to the same object pointed by the collection.
Design Lesson: if the clients of a system perform a particular operation very commonly (in this case,
iterating over all the elements in a collection) -- it's a good design idea to have a simple facility that
makes that common case very easy. It's ok if the facility does not handle more advanced uses. It can be
simple and focus just on the common case. Putting in such a special-purpose facility is, in a way,
inelegant -- it creates more than one way to do things. However, experience shows that making
common cases very easy is worthwhile.
// Can go back and forth between typed Collections and untyped "raw"
// forms -- may get a warning.
List<String> genList = new ArrayList(); // warning
List rawList = new ArrayList<String>(); // no warning
rawList.add("hello"); // warning
genList = rawList; // warning
rawList = genList; // no warning
In the simplest case, the class code doe not have any specific constraint on what type T is. Inside the
class, T may be used in limited ways:
- declare variables, parameters, and return types of the type T
- use = on T pointers
- call methods that work on all Objects, like .equals()
4
public Pair(T a, T b) {
this.a = a;
this.b = b;
}
public T getA() {
return a;
}
public T getB() {
return b;
}
You do not return a <?> type -- in that case you want a <T> in the return type and also somewhere else
specified by the client, such as a parameter. The <?> works for cases so simple that the <?> only
appears in one place. For multiple <>, use <T>.
// <?> Method -- Use a "wildcard" <?> on the parameter, which
// is basically a "don't care" marker. The <?> is not listed
// before the return type.
// We cannot declare variables of type ?, but we can use Object
// instead.
// We can declare local generic variables that mention <?>.
public static void removeAdjacent2(Collection<?> coll) {
Iterator<?> it = coll.iterator(); // Can use <?> in local variables
Object last = null; // Cannot use ? as a var type, use Object
while (it.hasNext()) {
Object curr = it.next();
if (curr == last)
it.remove();
last = curr;
}
}
public PairNumber(T a, T b) {
this.a = a;
this.b = b;
}
// The point: you can assign a pointer of type sub to a pointer of type
// super. However, you cannot assign a pointer type container(sub) to a
// pointer of type container(super).
// Therefore Collection<Object> will not work as a sort of catch-all
// type for any sort of collection.