Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Java 101: The next generation: The essential Java language features tour, Part 1

Java programming with assertions and generics

  • Print
  • Feedback

Page 7 of 9

George Smith: 15.20
Jane Jones: 25.60
John Doe: 35.40

What about lower bounds?

You cannot specify a lower bound for a generic type parameter. To understand why I recommend reading Angelika Langer's Java Generics FAQs on the topic of lower bounds, which she says "would be confusing and not particularly helpful."

Considering wildcards

Let's say you want to print out a list of objects, regardless of whether these objects are strings, employees, shapes, or some other type. Your first attempt might look like what's shown in Listing 7.

Listing 7. GenDemo.java (version 3)

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenDemo
{
   public static void main(String[] args)
   {
      List<String> directions = new ArrayList<String>();
      directions.add("north");
      directions.add("south");
      directions.add("east");
      directions.add("west");
      printList(directions);

      List<Integer> grades = new ArrayList<Integer>();
      grades.add(new Integer(98));
      grades.add(new Integer(63));
      grades.add(new Integer(87));
      printList(grades);
   }

   static void printList(List<Object> list)
   {
      Iterator<Object> iter = list.iterator();
      while (iter.hasNext())
         System.out.println(iter.next());
   }
}

It seems logical that a list of strings or a list of integers is a subtype of a list of objects, yet the compiler complains when you attempt to compile this listing. Specifically, it tells you that a list-of-string cannot be converted to a list-of-object, and similarly for a list-of-integer.

The error message you've received is related to the fundamental rule of generics:

 

For a given subtype x of type y, and given G as a raw type declaration, G<x> is not a subtype of G<y>.

According to this rule, although String and java.lang.Integer are subtypes of java.lang.Object, it's not true that List<String> and List<Integer> are subtypes of List<Object>.

Why do we have this rule? Remember that generics are designed to catch type-safety violations at compile time, which is helpful: without generics, you are much more likely to be called in to work at 2 a.m. because your Java program has thrown a ClassCastException and crashed!

As a demonstration, let's assume that List<String> was a subtype of List<Object>. If this was true, you might end up with the following code:

List<String> directions = new ArrayList<String>();
List<Object> objects = directions;
objects.add(new Integer());
String s = objects.get(0);

This code fragment creates a list of strings based on an array list. It then upcasts this list to a list of objects (which isn't legal, but for now just pretend it is). Next, it adds an integer to the list of objects, which violates type safety. The problem occurs in the final line, which throws ClassCastException because the stored integer cannot be cast to a string.

Without generics, your only option to prevent such a violation of type safety would be to pass an object of type List<Object> to the printList() method in Listing 7, which wouldn't be very useful. With generics, however, you can use wildcards to solve the problem, as shown in Listing 8.

Listing 8. GenDemo.java (version 4)

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenDemo
{
   public static void main(String[] args)
   {
      List<String> directions = new ArrayList<String>();
      directions.add("north");
      directions.add("south");
      directions.add("east");
      directions.add("west");
      printList(directions);

      List<Integer> grades = new ArrayList<Integer>();
      grades.add(new Integer(98));
      grades.add(new Integer(63));
      grades.add(new Integer(87));
      printList(grades);
   }

   static void printList(List<?> list)
   {
      Iterator<?> iter = list.iterator();
      while (iter.hasNext())
         System.out.println(iter.next());
   }
}

In Listing 8 I use a wildcard (the ? symbol) in place of Object in printList()'s parameter list and body. Because this symbol stands for any type, it's legal to pass List<String> and List<Integer> to this method.

Compile Listing 8 (javac GenDemo.java) and run the application (java GenDemo). You should observe the following output:

  • Print
  • Feedback

Resources
  • Download the source code for this article.
  • Read Angelika Langer's Java Generics FAQs for a wealth of information and perspective about generics in the Java language.
  • For students of the Java language and its controversies, Langer's "Und erstanding the closures debate" (JavaWorld, June 2008) compares the three initial proposals for adding closures, or lambda expressions, to the Java language in Java 7.
  • See "Java Reflection: Generics" (Jakob Jenkov, Jenkov.com) for further discussion about reflection with generics and special cases where it is possible to access generics information at runtime.
  • More from Java 101: The next generation:
  • More about the Java Collections Framework on JavaWorld: