An Overview Of The New Features in J2SE 5.0

J2SE 5.0 has been available for download since the end of 2004. This new release included many changes and enhancements to the Java platform such as speed and stability. Additionally, some changes were made to the Java language itself. These fairly major changes made to the language are:

  • Generics
  • Enhanced for loop
  • Annotations (sometimes called metadata)
  • Autoboxing and unboxing
  • Typesafe enumerations
  • Variable arguments (varargs)
  • Static imports

Using these new language features in your applications can have a big effect on your code, so this article aims to provide an overview of these new features so that you can start leveraging them in your code. As a Java developer I make extensive use of these features now and find that they bring the Java language much more upto date.

Generics

The latest version of J2SE could have a collection of any type of object). Prior to Java 5, if you wanted to extract the contents of a collection, you would use code such as that below:

import java.util.*;

public class Generic {

  public static void main(String[] args) {
    // Create a collection and add some items to
    // it.
    Collection languages = new ArrayList();
    languages.add("Java");
    languages.add("C#");
    // Now get the items from the collection.
    Iterator iterator = languages.iterator();
    while (iterator.hasNext()) {
      System.out.println(
         (String)iterator.next());
    }
  }
}

Notice that to extract objects from the collection, we have to explicitly cast the retrieved objects to be Strings (or whatever class the collection consists of), potentially allowing us to get the dreaded ClassCastException if we had inadvertently added the wrong type of object to the collection in the first place. Also, its difficult to see from looking at the code what the collection contains. If there was an API method like the following, what exactly would the collection contain?

public doStuff(Collection items);

Using generics, our simple application can be re-written to get rid of the cast and made type safe.

import java.util.*;

public class Generic2 {

  public static void main(String[] args) {

    // Create a collection and add some items to it.
    Collection<String> languages = new ArrayList<String>();
    languages.add("Java");
    languages.add("C#");

    // Now get the items from the collection.
    Iterator iterator = languages.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}

You can see that the collection has been explicitly declared to contain only String objects. Also note, that there is no cast required to extract items from the collection. The VM knows exactly what type of object is in the collection and knows how to return it to the calling application. If we tried to put something into the collection that isn't of the required type (in our small example, anything other than a String), then the code will simply fail to compile.

If we now re-wrote our rather obscure method doStuff, it may look something like the following. Here we can see exactly what type of object the collection contains.

public doStuff (Collection<String> items)

Enhanced for Loop

The enhanced for loop construct available in Java 5 allows us to refine this code even further and remove the need for using the Iterator to loop through the collection. This construct allows our simple application to be re-written, yet again, as:

import java.util.*;

public class Generic3 {

  public static void main(String[] args) {

    // Create a collection and add some items to it.
    Collection<String> languages = new ArrayList<String>();
    languages.add("Java");
    languages.add("C#");

    // Now get the items from the collection.
    for (String language : languages) {
      System.out.println(language);
    }
  }
}

This new construct is declaring that the code should loop through each entry in the collection languages. Each entry in the collection will be declared as a String called language which is then printed out to the console.

Hopefully you'­ll agree that these new features of Generics and the enhanced for-loop allow Java developers to write cleaner and safer code.

Annotations

Annotations provide a new feature to the Java language that potentially allow a vast amount of time to be saved when developing applications. They've been available in C# from the beginning and are now available to Java developers.

Annotations allow developers to add "hints" into their code (in a similar fashion to Xdoclet or JavaDoc tags) that the compiler can interpret at compile time. This allows the build process to do a variety of things such as produce artifacts that would otherwise have to be manually created, or to check that code complies with certain criteria.

There are 3 annotations that are supplied with J2SE 5.0, but developers have the ability to write additional annotations if they desire. These 3 basic annotations are @Override, @Deprecated and @Suppress.

@Override is applied to methods and is used by the compiler to check that overridden methods are declared correctly. A simple example of this would be to apply it to the toString() method of a class. @Override checks that the method to which it is applied correctly overrides its parent object. If the method is overridden incorrectly (e.g. it is spelt incorrectly or has the wrong signature) then a compile time error will be generated.

public class Annotation1 {

  @Override
  public String toStrng() {
    return "...";
  }
}

In this code fragment above, the method toString() has been declared incorrectly, e.g. public String toStrng() - note the deliberate spelling mistake. The @Override tag causes an error to be issued at compilation time.

C:>javac Annotation1.java
Annotation1.java:3: method does not override a method from its superclass
        @Override
         ^
1 error

@Deprecated is applied to methods in a similar fashion to @Override. This annotation will cause a compiler warning to be issued if a deprecated method is used. Finally, @Supress is used to tell the compiler to suppress specified warnings.

The use of annotations is particularly of importance in the J2EE arena and is becoming more important in the strive to make J2EE easier. A couple of examples of this are Webservices (JAX-WS 2.0) and Enterprise Java Beans (EJB 3). The J2EE 1.4 way of creating web service and EJBs involves creating several different interfaces and various verbose XML configuration files that describe the webservices and EJBs being developed. With JAX-WS annotations, it will be possible to declare that a class should be exposed as a web service simply by putting a @WebService annotation before the class declaration. With EJBs its very similar. To declare a class as a stateless session bean is simply a matter of putting the @Stateless annotation before the class declaration. Of course there are more options that can be added into web services and EJBs to control their behaviour, but the fundamental principal is that all these options can be specified in code as annotations.

Autoboxing / Unboxing

Autoboxing and unboxing allows Java code to automatically convert between the basic primitive types (int, long, double etc) and their class equivalents (Integer, Long, Double etc.) and vice-versa. Prior to Java 5, if you needed to convert an Integer to an int, the code required would look something like:

public class Autoboxing {

  public static void main(String[] args) {
    int value = 10;
    Integer newValue = new Integer(value);
    Autoboxing ab = new Autoboxing();
    ab.doStuff(newValue);
  }

  public void doStuff(Integer value) {
    System.out.println(value);
  }
}

In this code, the wrapper class newValue has to be directly instantiated from the variable value to be passed into the method. With Autoboxing, this is no longer required as the int will be converted into an Integer for us:

public class Autoboxing2 {

  public static void main(String[] args) {
    int value = 10;
    Autoboxing2 ab = new Autoboxing2();
    ab.doStuff(value);
  }

  public void doStuff(Integer value) {
    System.out.println(value);
  }
}

Static Import

Static imports are probably one of the least used features available to Java 5. They allows you to specify static variables in another class without having to specify the fully qualified name of the class being imported. Members can be statically imported into a class using the import static construct.

import static my.class.static.member;

Summary

Java 5 introduced new language features that allow developers to write code that is more robust whilst at the same time reducing the amount of boiler-plate code that needs to be written. If you'­re new to Java or are still using JDK 1.4, then its worth while investigating all the new features provided in Java 5.