This problem is most easily seen with the help of a simple concrete example.
public class MyPair
{
Object x, y;
public MyPair(Object o1, Object o2)
{
x = o1;
y = o2;
}
public Object first() { return x; }
public Object second() { return y; }
}
Now let's imagine we're using this:
MyPair p = new MyPair("ying","yang");
What is the type of p.first()?
Well it is an Object. That means the only methods that can be
called with it are the few common to every Object.
Nothing else is OK.
In our example, we know the result is actually a String, but
p.first().length()won't compile. Our only alternative is to cast, like this
String s = (String)p.first(); int n = s.length();Not the biggest deal in this small example, but it gets painful in bigger examples. More importantly, each of these casts could fail, because we may unwittingly put the wrong type of object into the MyPair. Nothing checks these types for us as we compile, so if we make such a mistake, it's as a runtime error, which is bad. (Imagine if this code was for a heart monitor?)
Java provides us with another way to handle these
problems: generics.
Generics allow you to write code where types become
parameters, i.e. a variable name refers to a type — like
int, String or
Double[ ]
— rather than referring to a
value of a fixed type. Type parameters are indicated by
<name>, where name is the
name of your type parameter. For example, below we rewrite
MyPair using a typeparameter named T, and after
the declaration, we literally search-and-replace "Object" with "T":
public class MyPair<T>
{
T x, y;
public MyPair(T o1, T o2)
{
x = o1;
y = o2;
}
public T first() { return x; }
public T second() { return y; }
}
To instantiate a MyPair that stores Strings, we give String as a
type argument for the type parameter T.
MyPair<String> p = new MyPair<String>("ying","yang");
It's important to note that the type of p is
MyPair<String>, i.e. the type argument is a
part of the full typename. Thus, the type of p is different
from MyPair<Scanner>, for example.
Moreover, MyPair by itself is not a type, since
no type for the typeparameter T is specified.
The big payoff in this is that the compiler enforces that only
Strings may be added to the pair p and, therefore, it knows
that the type of p.first() is String. Thus,
p.first().length()compiles no problem.
public class Queue<T> implements Iterable<T>and systematically search for String and replace it with T. If we do that, we get a Queue class that works for any type.
Now, if we want a Queue of Scanners it would be
Queue<Scanner> Q = new Queue<Scanner>();.
If we want a Queue of Strings it would be
Queue<String> Q = new Queue<String>();.
Here's a simple program that shows this fact off.
~/$ java Ex2 I am 23 years and 5 months old on 3 March 2015 I am years and months old on March 23 5 3 2015
~/$ java Ex3 I am 23 years and 5 months old on 3 March 2015 I am years and months old on March 23 5 3 2015