With the scheduled beta release of Java 2 Platform, Standard Edition 5.0 (J2SE 5.0) in late 2003, (known as project "Tiger") there is quite a buzz among developers about coming changes. No one is better positioned to explain J2SE 5.0 language changes than Joshua Bloch, a senior staff engineer at Sun Microsystems, Inc. Bloch, an architect in the Core Java Platform Group, designed and implemented the award-winning Java Collections Framework, the java.math package, and has contributed to many other parts of the platform. The author of numerous articles and papers, Bloch has also written a book, Effective Java Programming Language Guide, which won the prestigious Jolt Award from Software Development Magazine. Bloch holds a Ph.D. in computer science from Carnegie-Mellon University. We met with him to look into the future of the Java programming language.
The improvements to J2SE being developed in project "Tiger" are intended to make programs clearer, shorter, safer, and easier to develop, without sacrificing compatibility. Could you tell us how the Java language will be easier to work with in J2SE 5.0?
- Joshua Bloch, senior staff engineer, Sun Microsystems.
And the whole will be greater than the sum of its parts. All of the new features were designed with each other in mind. They interact harmoniously to provide a marked improvement in the expressiveness and safety of the language.
Which changes might be hardest for developers to adjust to? Or, what kinds of adjustments will they need to make?
I'm hopeful that none of the changes will be that hard for developers to adjust to. If I had to pick one that might require some adjustment, it would be generics, because you'll have to get used to providing additional information in declarations. Instead of merely saying:
List words = new ArrayList();
You'll have to say:
List<String> words = new ArrayList<String>();
The upside is that if you try to insert something that's not a string, you find out at compile time and fix the problem. Without generics, you discover such a bug when your most important customer calls your VP to tell him that the program on which his business depends just crashed with a ClassCastException.
The other upside is that you don't have to cast when you get an element out of the collection. So instead of:
String title = ((String) words.get(i)).toUppercase();
it's simply:
String title = words.get(i).toUppercase();
The beta release of J2SE 5.0 is scheduled for late 2003. These changes are all going through the Java Community Process (JCP). Two questions: How certain is it that the planned changes will actually be as currently conceived, and how can developers participate?
The expert groups for the relevant JSRs (14, 201, and 175) will certainly refine the draft proposals that are currently available, but I'm reasonably confident that the overall outlines of the proposals won't change much between now and the final release. Of course, the expert groups could prove me wrong.
The best way to participate is to read the drafts as they evolve, and to send comments to the relevant expert groups. The latest draft for generics can be found here:.
You can download a pre-release compiler that understands generics and start getting your feet wet today:.
Early drafts of other language proposals are available on this page:. Look in Section 3.1, near the bottom of the page.
There's no draft available yet for JSR-175 (metadata) but there will be soon. Watch this space for further developments:
Do you want to give us a simple take-home message for each of the six areas of improvement?
I'll give it a whirl...
for
int
Integer
Moving to specifics and starting with generics, what is the difference between filtering a collection today and filtering a collection with generics?
Here's how you do it today:
/**
* Remove the four-letter words from the specified
* collection, which must contain only strings.
*/
static void expurgate(Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); ) {
String s = (String) i.next();
if(s.length() == 4)
i.remove();
}
}
The casts aren't very pretty, and more importantly they can fail at run time. Suppose the user accidentally passes in a collection that contains string buffers rather than strings. The comment says that the client is required to pass in a collection of strings, but the compiler can't enforce the comment.
Here's how the same method looks with generics:
/**
* Remove the four-letter words from the specified collection of strings.
*/
static void expurgate(Collection<String> c) {
for (Iterator<String> i = c.iterator(); i.hasNext(); )
if (i.next().length() == 4)
i.remove();
}
Now it's clear from the method signature that the input collection must contain only strings. If the client tries to pass in a collection of string buffers, the program won't compile. And notice that the method doesn't contain any casts. It's one line shorter and, once you get used to reading generics, it's clearer, too.
Tell us about "enhanced for"?
Iterating over a collection is uglier than it needs to be. Most of the time when you iterate over a collection, you don't use the iterator for anything except getting elements. The "enhanced for" statement makes the compiler take care of the iterator for you. For example, here's a method that traverses a collection of timer tasks using an iterator:
void cancelAll(Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); ) {
TimerTask tt = (TimerTask) i.next();
tt.cancel();
}
}
Now here's the same method with an "enhanced for" statement:
void cancelAll(Collection c) {
for (Object o : c)
((TimerTask)o).cancel();
}
When you read the statement out loud, the colon (:) is pronounced "in." It might have been more natural to use two new keywords, foreach and in, but new keywords are destabilizing. They break existing sources that used the new keywords as identifiers. We went out of our way to preserve compatibility when we specified the new language extensions.
Can you combine generics and the "enhanced for" loop?
Absolutely! Here's how the previous example looks when you add generics
into the mix:
void cancelAll(Collection<TimerTask> c) {
for (TimerTask task : c)
task.cancel();
}
I think this is really pretty! Now the program says exactly what it does, and provides compile-time type safety to boot.
Tell us about autoboxing.
As you know, the Java programming language has a "split type system": some types are primitives and others are object references. You can't put primitives into collections, so you end up converting back and forth between primitives (such as int) and "wrapper types" (such as Integer) when you need to store them in collections. Anyone who has done this can attest that it isn't pretty.
For example, take a look at this program, which generates a frequency table of the words found on the command line. It uses a Map whose keys are the words and whose values are the number of times that each word occurs on the line:);
}
}
Notice how messy the inner-loop code that increments the count looks? Now take a look at the same program rewritten with autoboxing, generics, and an enhanced for loop:
public class Freq {
public static void main(String args[]) {
Map<String, Integer> m = new TreeMap<String, Integer>();
for (String word : args) {
Integer freq = m.get(word);
m.put(word, (freq == null ? 1 : freq + 1));
}
System.out.println(m);
}
}
Sweet, huh?.
NullPointerException
What are the advantages of the new typesafe enums over the standard approach, int enum pattern?
They're discussed in detail in Item 21 of my book, in connection with the
Typesafe Enum pattern. Very briefly:
Wow, that's powerful. How does the new typesafe enum language feature relate to the Typesafe Enum pattern in your book?
To a first approximation, the feature is simply linguistic support for the pattern. What took half a page of tedious error-prone code with the pattern now looks like the familiar C/C++ enum declaration:
enum Season { winter, spring, summer, fall }
But it doesn't act like a C/C++ enum! You get all of the cool features that I just mentioned. And it even fixes the major shortcoming of the Typesafe Enum pattern: you can use the new language feature in conjunction with switch statements.
Can you show me an example that demonstrates the more advanced capabilities of typesafe enums?
Sure, here's an enum that represents an American coin:
public enum Coin {
penny(1), nickel(5), dime(10), quarter(25);
Coin(int value) { this.value = value; }
private final int value;
public int value() { return value; }
}
See how the constant declarations invoke a constructor, passing in an int that represents the value in cents? And see how the value is stored in a private field with a public accessor? This just scratches the surface of what you can do.
Can you show me a class that uses the enum you just defined?
I was hoping you'd ask me that. Here's a little sample class that prints out a table of coins, their values, and their colors.
public class CoinTest {
public static void main(String[] args) {
for (Coin c : Coin.VALUES)
System.out.println(c + ": \t"
+ c.value() +"¢ \t" + color(c));
}
private enum CoinColor { copper, nickel, silver }
private static CoinColor color(Coin c) {
switch(c) {
case Coin.penny: return CoinColor.copper;
case Coin.nickel: return CoinColor.nickel;
case Coin.dime:
case Coin.quarter: return CoinColor.silver;
default: throw new AssertionError("Unknown coin: " + c);
}
}
}
See how we declare another enum for the colors? It doesn't cause any difficulty that there's a coin called nickel and a color called nickel, because coins and colors each get their own name space. Also, see how we can switch on enum constants? This is very useful when you want to "add a method" to an enum class, but you can't modify the class for whatever reason.
Looks good. What will the new static import facility offer developers?
It lets the programmer avoid prefixing static members with class names. People really want this, so much that they implement so-called constant interfaces to get the effect:
// "Constant Interface" antipattern - do not use!
public interface Physics {
public static final double AVOGADROS_NUMBER = 6.02214199e23;
public static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
public static final double ELECTRON_MASS = 9.10938188e-31;
}
public class Guacamole implements Physics {
public static void main(String[] args) {
double moles = ...;
double molecules = AVOGADROS_NUMBER * moles;
...
}
}
This is a very bad idea: interfaces are for defining types, not providing constants. The fact that the Guacamole class uses the Physics constants is just an implementation detail, but it "leaks" into the public API of the Guacamole class. Not only does this confuse the clients of this class, but it creates a long-term commitment. Even if you re-implement the Guacamole class so that it doesn't need these constants, you still have to implement the interfaces, as clients of the Guacamole class can now depend on the fact that it implements Physics.
Guacamole
Physics
The static import facility offers a clean alternative. It lets the programmer avoid qualifying static member names without subtyping. It's analogous to the package import facility, except that it imports static members from a class, rather than classes from a package. Here's how it looks:
import static org.iso.Physics.*;
class Guacamole {
public static void main(String[] args) {
double molecules = AVOGADROS_NUMBER * moles;
...
}
}
Note that this works whether Physics is an interface or a class. If it just defines constants, it should definitely be a class rather than an interface.
Got it. Now could you tell us about the new metadata facility?
It's a bit different from the other features we've discussed. It's also focused on making life easier for the developer, but with the assistance of tools vendors.
These days, many APIs require a fair amount of boilerplate. For example, when you define a JAX-RPC Web Service you provide both an interface and an implementation class:) {
...
}
}
This example was copied straight from our Web Services Tutorial.
With the metadata facility, you don't have to write all of this yourself. You just annotate the code to let a tool know which methods are remotely accessible, and the tool generates the above code. Here's how the source code looks with metadata:
import javax.xml.rpc.*;
public class CoffeeOrder {
@Remote public Coffee [] getPriceList() {
...
}
@Remote public String orderCoffee(String name, int quantity) {
...
}
}
All the boilerplate's gone!
Yes, that's much clearer. But you can't possibly define all of the useful attributes and build all the tools, can you?
No, JSR-175 is just providing the framework that enables others to define attributes and build tools. Other JSRs -- such as JSR-181, which is defining metadata for Web Services -- are defining attributes. We expect lots of activity in this area.
Looking further down the road, what future changes to the Java language do you anticipate?
It's hard to say. I'm too busy working on Tiger to think about what comes next.
Is there any message you want to leave us with?
Above all else, Tiger is a developer-focused release. When the Java programming language was introduced by James Gosling and his team, it took off like a rocket because it struck a chord with developers. This release is all about building on that legacy.
I've had the good fortune to play with early prototypes of the new language features, and I find them a joy to use. I have every confidence that other developers will too. The new features add more pleasure and productivity to what was already one of the most pleasurable and productive programming languages around.
Early Drafts Proposals for J2SE 5.0
Draft for Generics
Pre-release Compiler Download
J2SE
Effective Java Programming Language Guide