Return to Course Content Home

Generics

All right, by now you are probably totally overwhelmed. This is effectively an optional section as you may only touch on Generics if you use certain Collections or other classes with Generic arguments. Teaching you how to fully use Generics is completely out of scope for this course.

Optional Reading

If you feel so inclined, read The Java Tutorial on Generics

Important note on images in this module

Please note that the screenshots in this section were made with an earlier version of the JDK and Elipse. Since everything covered in this module still works the same in the latest JDK/Eclipse versions, they have not been updated as they fully apply.

Why Generics?

Actually, the need for Generics was pretty obvious. Up until JDK 5, most Collections (like Vector) just took Objects as an argument. While this allowed you to create a Vector of whatever you wanted (good), it let you create a Vector of whatever you wanted (bad).

Vector had no problem adding a Dog, Person, PayrollRecord and Color object to the same Vector list. It was up to you to make sense of things later when you tried to use the Objects. Unfortunately, this didn't provide any type safety, and the compiler was powerless to help you spot errors during compile time.

With Generics, you can still use abstraction over types, but you can at least make sure that you are getting what you expect in a Collection.

So, without Generics, you used to do something like this:

List employeeList = new List();
employeeList.add(new Employee()); ... employeeList.add(new Employee()); Employee emp = (Employee)employeeList.iterator().next();

Note that when we pulled an Employee from the iterator, it came out as an Object, and we had to cast the Object to the Employee class. If we had added some other class to the List, this code wouldn't work.

With Generics, we can specify a type as part the List definition

List<Employee> employeeList = new List<Employee>();
employeeList.add(new Employee());
...
employeeList.add(new Employee());
Employee emp = employeeList.iterator().next();

Notice how we now expect to get an Employee out of employeeList, rather than just an Object. More importantly, if we tried to add a Car object to the employeeList, it would not compile, but in the earlier version, it could be added without any problems.

Wildcards

While the Generic types are helpful for Containers, it is just as important to be able to write methods that handle Generics as well. Granted we can write a method that takes an List as an argument

void printList(List<String> c) {
  for (String s : c) {
     System.out.println(s.toString());
  }
}
 


Can we pass an List of Employees to this method? The answer unfortunately is no. However you can provide a "wildcard" that matches List and all types of List .

void printList(List<?> c) {
  for (Object e : c) {
     System.out.println(e.toString());
  }
}

In this case, printCollection can take a List of any type and process it.

Generic Methods

This example is out of the Java Tutorial, but it's a good one, so I'll use it here.

What if we wanted to make a method that converted an array of objects into a Collection of objects? The tutorial provides a "potential" solution by using something like this:

static void fromArrayToCollection(Object[] a, Collection<?> c) {
      for (Object o : a) {
           c.add(o); // compile-time error
      }
}

While the <?> wildcard worked above for accessing a wildcard type of Generic, it turns out you can't make changes to a Collection using a wildcard definition, hence the "// compile-time error" annotation above.

The way to get around this is to use Generic methods. Convention has you using the variable "T" for a Generic type in this situation (just like we use "i" for an integer counter)

So, you can use the "T" designation to write the method like this. (Note the <T> at the beginning of the definition after the static keyword but before the void return type):

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
      for (T o : a) {
          c.add(o); // Correct
      }  
}

Now we can call this method with any type of Class we want, and basically it substitutes the class you provide with the "T" variable. So if you use a String.

String[] a = {"foo", "bar", "baz};
ArrayList<String> ls = new ArrayList<String>();
fromArrayToCollection(a, ls);

When you do this, you are basically creating a method that reads like this:

static void fromArrayToCollection(String[] a, Collection<String> c) {
      for (String o : a) {
          c.add(o); // Correct
      }  
}

Now, there is much more to Generic methods, but for our class. These simple examples should cover anything you need for our projects. Feel free to read more about Generics, as they are very powerful, but we have to keep in mind, this is just a one week module!!!

 

Practice

Download the Eclipse project ArrayListNonGeneric and load it into Eclipse or at least view the source code.

Eclipse gives you a warning (although it is somewhat difficult to make out) by underlining the text and placing a marker at the left of the window

Warning markers

This is because ArrayList can still be used without Generics, but it isn't as "safe" as using Generics. Also note that since without using Generics, ArrayList accepts type Objects as arguments, so before you can use the objects as their actual types you need to recast things.

If you play around, you could also insert the following to the ArrayList

list.add(new Integer(5));

The problem is that you only wanted Strings in the list, but programmatically, you can add any Objects at all.

Now download and load the Exlipse Project ArrayListGeneric.

Try running the file. Note that when the file is now compiled, there are no warning messages. Take a look at the line:

ArrayList<String> list = new ArrayList<String>();

This is creating the ArrayList with elements of type String. If you tried to add an Integer to this list, you would get a compilation error.

Also note that when we get the values from the list in the second loop, we no longer have to cast the returned value to a String. That's because the ArrayList knows it has Strings in it.

String s = list.get(i);

Array List with Generics

If you have any questions or comments, please post them to the Discussion forum.