Generics in the java programming language, tutoriel language java document pdf.
1 Introduction
2 Defining Simple Generics
3 Generics and Subtyping
4 Wildcards
4.1 Bounded Wildcards
5 Generic Methods
6 Interoperating with Legacy Code
6.1 Using Legacy Code in Generic Code
6.2 Erasure and Translation
6.3 Using Generic Code in Legacy Code
7 The Fine Print
7.1 A Generic Class is Shared by all its Invocation
7.2 Casts and Instance Of
7.3 Arrays
8 Class Literals as Run-time Type Tokens
9 More Fun with Wildcards
9.1 Wildcard Capture
10 Converting Legacy Code to Use Generics
11 Acknowledgements
Introduction
JDK 1.5 introduces several extensions to the Java programming language. One of these is the introduction of generics.
This tutorial is aimed at introducing you to generics. You may be familiar with similar constructs from other languages, most notably C++ templates. If so, you’ll soon see that there are both similarities and important differences. If you are not familiar with look-a-alike constructs from elsewhere, all the better; you can start afresh, without unlearning any misconceptions.
Generics allow you to abstract over types. The most common examples are container types, such as those in the Collection hierarchy.
Here is a typical usage of that sort:
List myIntList = new LinkedList(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = (Integer) myIntList.iterator().next(); // 3
The cast on line 3 is slightly annoying. Typically, the programmer knows what kind of data has been placed into a particular list. However, the cast is essential. The compiler can only guarantee that an Object will be returned by the iterator. To ensure the assignment to a variable of type Integer is type safe, the cast is required.Of course, the cast not only introduces clutter. It also introduces the possibility of a run time error, since the programmer might be mistaken.What if programmers could actually express their intent, and mark a list as being restricted to contain a particular data type? This is the core idea behind generics. Here is a version of the program fragment given above using generics:
List<Integer> myIntList = new LinkedList<Integer>(); // 1’
myIntList.add(new Integer(0)); //2’
Integer x = myIntList.iterator().next(); // 3’
Defining Simple Generics
Here is a small excerpt from the definitions of the interfaces List and Iterator in pack-age java.util:
public interface List<E> {
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E> {
E next();
boolean hasNext();
}
This should all be familiar, except for the stuff in angle brackets. Those are the declarations of the formal type parameters of the interfaces List and Iterator.Type parameters can be used throughout the generic declaration, pretty much whereyou would use ordinary types (though there are some important restrictions; see section 7).
In the introduction, we saw invocations of the generic type declaration List, such as List<Integer>. In the invocation (usually called a parameterized type), all occurrences of the formal type parameter (E in this case) are replaced by the actual type argument (in this case, Integer).
You might imagine that List<Integer> stands for a version of List where E has been uniformly replaced by Integer:
public interface IntegerList {
void add(Integer x)
Iterator<Integer> iterator();
}
This intuition can be helpful, but it’s also misleading.
It is helpful, because the parameterized type List<Integer> does indeed have methods that look just like this expansion.
Generics and Subtyping
Let’s test our understanding of generics. Is the following code snippet legal?
List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2
Line 1 is certainly legal. The trickier part of the question is line 2. This boils down to the question: is a List of String a List of Object. Most people’s instinct is to answer:
“sure!”.
Well, take a look at the next few lines:
lo.add(new Object()); // 3
String s = ls.get(0); // 4: attempts to assign an Object to a String!
Here we’ve aliased ls and lo. Accessing ls, a list of String, through the alias lo, we can insert arbitrary objects into it. As a result ls does not hold just Strings anymore,and when we try and get something out of it, we get a rude surprise.
The Java compiler will prevent this from happening of course. Line 2 will cause a compile time error.
In general, if Foo is a subtype (subclass or subinterface) of Bar, and G is some generic type declaration, it is not the case that G<Foo> is a subtype of G<Bar>. This is probably the hardest thing you need to learn about generics, because it goes against our deeply held intuitions.
The problem with that intuition is that it assumes that collections don’t change.
Our instinct takes these things to be immutable.
For example, if the department of motor vehicles supplies a list of drivers to the census bureau, this seems reasonable. We think that a ist<Driver> is a List<Person>,assuming that Driver is a subtype of Person. In fact, what is being passed is a copy of the registry of drivers. Otherwise, the census bureau could add new people who are not drivers into the list, corrupting the DMV’s records.
Wildcards
Consider the problem of writing a routine that prints out all the elements in a collection.
Here’s how you might write it in an older version of the language:
void printCollection(Collection c) {
Iterator i = c.iterator();
for (k = 0; k < c.size(); k++) {
System.out.println(i.next());
}}
And here is a naive attempt at writing it using generics (and the new for loop syntax):
void printCollection(Collection<Object> c) {
for (Object e : c) {
System.out.println(e);
}}
The problem is that this new version is much less useful than the old one. Where as the old code could be called with any kind of collection as a parameter, the new code only takes Collection<Object>, which, as we’ve just demonstrated, is not a super type of all kinds of collections!
So what is the supertype of all kinds of collections? It’s written Collection<?>
(pronounced “collection of unknown”) , that is, a collection whose element type matches anything. It’s called a wildcard type for obvious reasons. We can write:
void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}}
and now, we can call it with any type of collection. Notice that inside printCollection(), we can still read elements from c and give them type Object. This is always safe, since whatever the actual type of the collection, it does contain objects. It isn’t safe to add arbitrary objects to it however:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // compile time error
Bounded Wildcards
Consider a simple drawing application that can draw shapes such as rectangles and circles. To represent these shapes within the program, you could define a class hierarchy such as this:
public abstract class Shape {
public abstract void draw(Canvas c);
}
public class Circle extends Shape {
private int x, y, radius;
public void draw(Canvas c) { … }
}
public class Rectangle extends Shape {
private int x, y, width, height;
public void draw(Canvas c) { … }
}
These classes can be drawn on a canvas:
public class Canvas {
public void draw(Shape s) {
s.draw(this);
}
}
Any drawing will typically contain a number of shapes. Assuming that they are represented as a list, it would be convenient to have a method in Canvas that draws them all:
public void drawAll(List<Shape> shapes) {
for (Shape s: shapes) {
s.draw(this);
}
}
Now, the type rules say that drawAll() can only be called on lists of exactly Shape:
it cannot, for instance, be called on a List<Circle>. That is unfortunate, since all the method does is read shapes from the list, so it could just as well be called on a List<Circle>. What we really want is for the method to accept a list of any kind of shape:
…
Java programming language (494 Ko) (Cours PDF)