The Future: Java 1.5 "Tiger"

A lot of potential technology is coming in Java 1.5, code-named "Tiger." However, we won't know what's actually included until it appears. At the moment, it's targeted to ship in the first half of 2004. In this section, we cover some of the new bits of technology planned for Tiger and also how to use some of the new Java language additions.

Generics (JSR 14)

Unlike some languages, in Java, every object is a subclass of the Object class. Because of this, the collection classes (List, Set, Map, and so on) were designed to contain Objects so that any object could be contained in a collection. However, working with collections in this manner has its problems. Consider this example:

// this list can hold any Object List stringList = new ArrayList();
stringList.add("Hello");
stringList.add("World");
// this will cause a ClassCastException later stringList.add(new Integer(13));
for (int i=0; i<stringList.size(); i++) {
 String s = (String)stringList.get(i);
 System.out.println(s);
}


In this code, the string list can hold any type of object, not just instances of the String object. If a different class, such as an Integer, is added to the list, during runtime the VM would throw ClassCastException when you try to cast that object to a String. Of course, this problem can be solved with more attentive programming, but no one is perfect, right? Generics, also called parameterized types or templates, offers to solve this problem. Generics is an upcoming addition to the Java language that will enable you to create a group of classes that have the same behavior but work with different types. As an example, the List collection class has similar behavior for whatever objects it contains, but you could define a specific List to work with different types such as Strings or Integers. This moves the type-checking to compile time rather than runtime. In code, you would create a List<String> or a List<Integer> instead of a List. Consider the same example as before, but with generics:

// this list can only hold Strings List<String> stringList = new ArrayList<String>();
stringList.add("Hello");
stringList.add("World");
// this would cause a compile-time error
//stringList.add(new Integer(13));
for (int i=0; i<stringList.size(); i++) {
 String s = stringList.get(i);
 System.out.println(s);
}


In this example, the string list can contain only String objects. If an Integer is added to the list, the source code wouldn't compile. Also, the get() method of a List<String> returns a String, so you don't have to cast to a String in the source code. Altogether, using generics gives these advantages:

  • Safety (stricter compile-time checks).
  • Code that is easier to read. (You know what types of elements a collection should contain.)
  • Less casting.

NOTE Using generics doesn't require any special bytecodes. The Java compiler will generate code that is backward-compatible with older VMs. So, the casting is still there in the compiled class.


The drawback to how generics are implemented is that they do not work with primitive types such as ints and floats. So, you'll either have to wrap primitive types in wrapper classes (Integer or Float) or implement your own collections for working with primitive types.

Enumerations (JSR 201)

In a lot of code in this tutorial, we use integers to define enumerations, like this:

public static final int STATE_IDLE = 0;
public static final int STATE_ACTIVE = 1;
public static final int STATE_DESTROYED = 2;


This works but has its problems. Take a look at the basic set method:

public void setState(int state) {
 // doesn't check for illegal values!
 this.state = state;
}


First, in this case, illegal values aren't checked. Some rogue code could set the state as 42. Optionally, you could check whether the value sent to this method is legal, as long as you maintained this code if the enumerations changed. Also, for documentation purposes, you must explicitly state the valid values for this method. Type-safe enumerations will fix this issue. Enumerations in Java will be defined similarly to how they are in C:

public enum State { IDLE, ACTIVE, DESTROYED }


In Java, however, enumerations are objects. The setState() method could be rewritten as follows:

public void setState(State state) {
 // null is only illegal value
 if (state == null) {
 throw new NullPointerException("Null State");
 }
 this.state = state;
}


Each value is its own instance, so you can still use the == operator:

if (state == State.IDLE) {
 state = State.ACTIVE;
}


To sum it up, enumerations give you similar productivity advantages as generics do:

  • Safety (no illegal enumerations).
  • Code that is easier to read. (Defining enumerations takes less code, and you do not need to document "legal" values.)
  • Code that is easier to maintain. If additional values are added to the enumeration, no changes need to be made for illegal values.

Static Import (JSR 201)

Consider invoking a couple static methods like this:

System.out.println(Math.abs(-1));


The basic idea with static import is to provide easier-to-read code. With the upcoming static import language addition, you could import the static classes:

import static System.*;
import static Math.*;


Then you use their fields and methods directly, as if they were members of the class you are writing:

out.println(abs(-1));


This helps with readability. However, static import could confuse others in some cases as well. For example, another programmer might have trouble finding which class a particular method or field comes from. Be sure to use this feature when you're certain it actually helps readability, not when it could be confusing.

Enhanced for Loop (JSR 201)

Also part of JSR 201 is a language enhancement of the for loop when iterating over collections and arrays. This enhancement makes code easier to write, cleaner, and more readable. Currently, iterating a list involves something like this (with generics):

for (Iterator<String> i = stringList.iterator(); i.hasNext();) {
 String s = i.next();
 System.out.println(s);
}


But with the enhanced for loop, the loop can be written like this:

for (String s : stringList) {
 System.out.println(s);
}


Similarly, for an array (in this example, an int array), it can be written like this:

int sum = 0;
for (int value : intArray) {
 sum += value;
}


Compiler API (JSR 199)

The new compiler API is designed to make compiling source code within Java apps easier. Instead of using the command line to call the compiler and then parsing the results, the compiler API will allow Java apps to interact with the Java compiler directly and then retrieve the compilation results as structured data. This will help if you want to do runtime compilation of game scripts. For example, you could compile Java source for a particular map directly within a map editor.

Network Transfer Format (JSR 200)

Currently, Java apps are typically compressed and distributed in JAR files. JAR files are basically ZIP files. ZIP files contain files that are individually compressed and then combined into one file. The compression used in ZIP files is a dictionary-based compression technique in which common symbols found in the file are encoded to smaller, variable-length bit strings and the symbols are stored in a table. JAR files are typically composed of dozens, hundreds, or even thousands of class files. Unfortunately, compressing each class file individually doesn't get as high compression rates as if the class files were compressed as a whole. For example, consider the uncompressed rt.jar file included with J2SE 1.4.1. This file is about 22.4MB in size, but if it were a compressed JAR, it would be about 11.6MB. However, using tar and bzip2 (common UNIX tools) to combine and compress the class files contained in rt.jar results in a file about 6.2MB in size. The network transfer format is designed to overcome the compression shortcomings of JAR files. It uses a few tricks that are purposely designed to compress class files. Users will be able to download smaller files that can be converted to standard JAR files on the local machine.

Shared VM (JSR 121)

Highly anticipated in Tiger is the shared virtual machine. A shared VM means one VM runs all Java apps, instead of starting a new VM for every app. This will save memory because only one heap is used, and Java apps will have a faster startup time if the VM is already running. JSR 121 helps define how to isolate Java apps within this shared VM so that they cannot interfere with one another.



   
Comments