Home arrow Java arrow Generics and Limitations in Java

Generics and Limitations in Java

Last week we began discussing generics and relationships in Java. This week, we'll learn about parameter type limitations, bounds, wildcards, and more. This article, the third in a series, was excerpted from chapter eight of the book Learning Java, third edition, written by Patrick Niemeyer and Jonathan Knudsen (O'Reilly; ISBN: 0596008732). Copyright 2006 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Author Info:
By: O'Reilly Media
Rating: 5 stars5 stars5 stars5 stars5 stars / 9
May 24, 2007
  1. · Generics and Limitations in Java
  2. · Bounds
  3. · Erasure and Bounds (Working with Legacy Code)
  4. · Wildcards
  5. · Thinking Outside the Container

print this article

Generics and Limitations in Java
(Page 1 of 5 )

Parameter Type Limitations

We have seen the parameter types (type variables) of a generic class used to declare instance variables, method arguments, and return types as well as "passed along" to parameterize a generic superclass. One thing that we haven't talked about is the question of how or whether we can use the type variable of a generic class to construct instances of the parameter type or work with objects of the type in other concrete ways. We deliberately avoided this issue in our previous "exception tester" example by simply passing our exception object in as an argument. Could we have done away with this argument? The answer, unfortunately, is that due to the limitations of erasure, there really is no parameter-type information to work with at runtime. In this section, we'll look at this problem and a workaround.

Since the type variable T has faithfully served as our parameter type everywhere else, you might imagine that we could use it to construct an instance of T using the new keyword. But we can't:

  T element = new T(); // Error! Invalid syntax.

Remember that all type information is erased in the compiled class. The raw type does not have any way of knowing the type of object you want to construct at runtime. Nor is there any way to get at the Class of the parameter type through the type variable, for the same reason. So reflection won't help us here either. This means that, in general, generics are limited to working with parameter types in relatively hands-off ways (by reference only). This is one reason that generics are more useful for containers than for some other applications. This problem comes up often though and there is a solution, although it's not quite as elegant as we'd like.

Using Class<T>

The only real way to get the type information that we need at runtime is to have the user explicitly pass in a Class reference, generally as one of the arguments to a method. Then we can explicitly refer to the class using reflection and create instances or do whatever else is necessary. This may sound like a really bad solution, without much type safety and placing a big burden on the developer to do the right thing. Fortunately, we can use a trick of generics to enforce this contract with the user and make it safe. The basic idea again is to have one of our methods accept the Class of the parameter type so that we can use it at runtime. Following our "exception tester" example:

  public void test( Class type ) throws T { ... }

This isn't much better than it was before. Specifically, it doesn't guarantee that the Class type passed to the method will match the parameterized type of the class (used in the throws clause here).

Fortunately, Java 5.0 introduced some changes in the Class class. Class is, itself, now a generic type. Specifically, all instances of the Class class created by the Java VM are instantiated with their own type as a parameter. The class of the String type, for example, is now Class<String>, not just some arbitrary instance of the raw Class type that happens to know about strings.

This has two side effects. First, we can specify a particular instantiation of Class using the parameter type in our class. And second, since the Class class is now generic, all of the reflective and instance creation methods can be typed properly and no longer require casts, so we can write our test() method like this:

  public void test( Class<T> type ) throws T {
     throw type.newInstance();

The only Class instance that can be passed to our test() method now is Class<T>, the Class for the parameter type, T, on which we instantiated ExceptionTester. So, although the user still has the burden of passing in this seemingly extraneous Class argument, at least the compiler will ensure that we do it and do it correctly:

  ExceptionTester<ArithmeticException> et =
      new ExceptionTester<ArithmeticException>();

  et.test( ArithmeticException.class ); // no other .class will work

In this code snippet, attempting to pass any other Class argument to the test() method generates a compile-time error.

Next: Bounds >>

blog comments powered by Disqus

- Java Too Insecure, Says Microsoft Researcher
- Google Beats Oracle in Java Ruling
- Deploying Multiple Java Applets as One
- Deploying Java Applets
- Understanding Deployment Frameworks
- Database Programming in Java Using JDBC
- Extension Interfaces and SAX
- Entities, Handlers and SAX
- Advanced SAX
- Conversions and Java Print Streams
- Formatters and Java Print Streams
- Java Print Streams
- Wildcards, Arrays, and Generics in Java
- Wildcards and Generic Methods in Java
- Finishing the Project: Java Web Development ...

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 

Developer Shed Affiliates


© 2003-2019 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials