generics in the java programming language cis1xx/resources/generics-  · generics in the java...

Download Generics in the Java Programming Language cis1xx/resources/generics-  · Generics in the Java Programming…

Post on 14-Jul-2018

213 views

Category:

Documents

0 download

Embed Size (px)

TRANSCRIPT

  • Generics in the Java Programming Language

    Gilad Bracha

    July 5, 2004

    Contents

    1 Introduction 2

    2 Defining Simple Generics 3

    3 Generics and Subtyping 4

    4 Wildcards 54.1 Bounded Wildcards . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

    5 Generic Methods 7

    6 Interoperating with Legacy Code 106.1 Using Legacy Code in Generic Code . . . . . . . . . . . . . . . . . . 106.2 Erasure and Translation . . . . . . . . . . . . . . . . . . . . . . . . . 126.3 Using Generic Code in Legacy Code . . . . . . . . . . . . . . . . . . 13

    7 The Fine Print 147.1 A Generic Class is Shared by all its Invocations . . . . . . . . . . . . 147.2 Casts and InstanceOf . . . . . . . . . . . . . . . . . . . . . . . . . . 147.3 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

    8 Class Literals as Run-time Type Tokens 16

    9 More Fun with Wildcards 189.1 Wildcard Capture . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

    10 Converting Legacy Code to Use Generics 20

    11 Acknowledgements 23

    1

  • 1 Introduction

    JDK 1.5 introduces several extensions to the Java programming language. One of theseis the introduction of generics.

    This tutorial is aimed at introducing you to generics. You may be familiar withsimilar constructs from other languages, most notably C++ templates. If so, youll soonsee that there are both similarities and important differences. If you are not familiarwith look-a-alike constructs from elsewhere, all the better; you can start afresh, withoutunlearning any misconceptions.

    Generics allow you to abstract over types. The most common examples are con-tainer types, such as those in the Collection hierarchy.

    Here is a typical usage of that sort:

    List myIntList = new LinkedList(); // 1myIntList.add(new Integer(0)); // 2Integer x = (Integer) myIntList.iterator().next(); // 3

    The cast on line 3 is slightly annoying. Typically, the programmer knows whatkind of data has been placed into a particular list. However, the cast is essential. Thecompiler can only guarantee that an Object will be returned by the iterator. To ensurethe 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 ofa run time error, since the programmer might be mistaken.

    What if programmers could actually express their intent, and mark a list as beingrestricted to contain a particular data type? This is the core idea behind generics. Hereis a version of the program fragment given above using generics:

    List myIntList = new LinkedList(); // 1myIntList.add(new Integer(0)); //2Integer x = myIntList.iterator().next(); // 3

    Notice the type declaration for the variable myIntList. It specifies that this is notjust an arbitrary List, but a List of Integer, written List. We say that List isa generic interface that takes a type parameter - in this case, Integer. We also specifya type parameter when creating the list object.

    The other thing to pay attention to is that the cast is gone on line 3.Now, you might think that all weve accomplished is to move the clutter around.

    Instead of a cast to Integer on line 3, we have Integer as a type parameter on line 1.However, there is a very big difference here. The compiler can now check the typecorrectness of the program at compile-time. When we say that myIntList is declaredwith type List, this tells us something about the variable myIntList, whichholds true wherever and whenever it is used, and the compiler will guarantee it. Incontrast, the cast tells us something the programmer thinks is true at a single point inthe code.

    The net effect, especially in large programs, is improved readability and robustness.

    2

  • 2 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 {void add(E x);Iterator iterator();

    }public interface Iterator {E next();boolean hasNext();

    }

    This should all be familiar, except for the stuff in angle brackets. Those are thedeclarations 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 section7).

    In the introduction, we saw invocations of the generic type declaration List, suchas List. In the invocation (usually called a parameterized type), all occur-rences of the formal type parameter (E in this case) are replaced by the actual typeargument (in this case, Integer).

    You might imagine that List stands for a version of List where E hasbeen uniformly replaced by Integer:

    public interface IntegerList {void add(Integer x)Iterator iterator();

    }

    This intuition can be helpful, but its also misleading.It is helpful, because the parameterized type List does indeed have

    methods that look just like this expansion.It is misleading, because the declaration of a generic is never actually expanded in

    this way. There arent multiple copies of the code: not in source, not in binary, not ondisk and not in memory. If you are a C++ programmer, youll understand that this isvery different than a C++ template.

    A generic type declaration is compiled once and for all, and turned into a singleclass file, just like an ordinary class or interface declaration.

    Type parameters are analogous to the ordinary parameters used in methods or con-structors. Much like a method has formal value parameters that describe the kinds ofvalues it operates on, a generic declaration has formal type parameters. When a methodis invoked, actual arguments are substituted for the formal parameters, and the methodbody is evaluated. When a generic declaration is invoked, the actual type argumentsare substituted for the formal type parameters.

    A note on naming conventions. We recommend that you use pithy (single characterif possible) yet evocative names for formal type parameters. Its best to avoid lower

    3

  • case characters in those names, making it easy to distinguish formal type parametersfrom ordinary classes and interfaces. Many container types use E, for element, as inthe examples above. Well see some additional conventions in later examples.

    3 Generics and Subtyping

    Lets test our understanding of generics. Is the following code snippet legal?

    List ls = new ArrayList(); //1List lo = ls; //2

    Line 1 is certainly legal. The trickier part of the question is line 2. This boils downto the question: is a List of String a List of Object. Most peoples instinct is to answer:sure!.

    Well, take a look at the next few lines:

    lo.add(new Object()); // 3String s = ls.get(0); // 4: attempts to assign an Object to a String!

    Here weve aliased ls and lo. Accessing ls, a list of String, through the alias lo, wecan 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 acompile time error.

    In general, if Foo is a subtype (subclass or subinterface) of Bar, and G is somegeneric type declaration, it is not the case that G is a subtype of G.This is probably the hardest thing you need to learn about generics, because it goesagainst our deeply held intuitions.

    The problem with that intuition is that it assumes that collections dont change.Our instinct takes these things to be immutable.

    For example, if the department of motor vehicles supplies a list of drivers to the cen-sus bureau, this seems reasonable. We think that a List is a List,assuming that Driver is a subtype of Person. In fact, what is being passed is a copyof the registry of drivers. Otherwise, the census bureau could add new people who arenot drivers into the list, corrupting the DMVs records.

    In order to cope with this sort of situation, its useful to consider more flexiblegeneric types. The rules weve seen so far are quite restrictive.

    4

  • 4 Wildcards

    Consider the problem of writing a routine that prints out all the elements in a collection.Heres 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 syn-tax):

    void printCollection(Collection c) {for (Object e : c) {

    System.out.println(e);}}

    The problem is that this new version is much less useful than the old one. Whereasthe old code could be called with any kind of collection as a parameter, the new codeonly takes Collection, which, as weve just demonstrated, is not a supertypeof all kinds of collections!

    So what is the supertype of all kinds of collections? Its written Collection(pronounced collection of unknown) , that is, a collection whose element type matchesanything. Its 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 printCollec-tion(), we can still read elements from c and give them type Object. This is alwayssafe, since whatever the actual type of the collection, it does contain objects. It isntsafe to add arbitrary objects to it however:

    Collection c = new ArrayList();c.add(new Object()); // compile time error

    Since we dont know what the element type of c stands for, we cannot add objectsto it. The add() method takes arguments of