Java enhancements, like generics and asserts, improve this already cool programming language June 8, 2001— As an object-oriented, network-enabled, platform-spanning language smart enough to include primitive data types for efficiency, Java has always been cool. But it just keeps getting better!Once you code in a few different languages, you come to realize just how important the language is to solving a given problem. When you’re using the right language, some puzzles just fall apart. But as someone who once tutored freshman who were ill-fated enough to take a class that forced them to solve real-world business problems using FORTRAN, I can tell you that the wrong language can turn a molehill of a problem into a nearly insurmountable mountain.And, for all its beauty, Java experienced a few problems of its own. One of the major gaffes was the inconsistent, persnickety mechanisms for looping over arrays, vectors, and strings. Sun Microsystems Senior Staff Engineer Joshua Bloch came to the rescue on that front with the elegant Collections API. Just as Java itself is platform-spanning, Collections present a mechanism-spanning API, thereby creating uniform, consistent mechanisms for writing code. The latest language improvements include generics and assertions. In addition, a variety of application templates and design patterns have become available to help you write better code more quickly. This article explores these enhancements:GenericsAssertionsEffective JavaJ2EE BlueprintsCommon interface standardsGenericsGilad Bracha, the specification lead and computational theologist for Sun, announced that the generics facility will arrive in the Tiger release of the Java platform (version 1.5). The bad news is that 1.5 is not due to be available until 2003. The good news is that an early-access version is available now, and you can actually make use of the technology in some practical ways. It turns out that Merlin’s (JDK 1.4) compiler is the generic-capable compiler, only with generics disabled. That means that the generic facility will undoubtedly be stable by the time it arrives. But it also means we’ll have to wait a fairly long time for something that is (almost) here today.(Note: For those familiar with the generics landscape, Bracha mentioned that Java’s generics facility is based on Pizza and Generic Java, and that much of the documentation on those precursors (available on the Web) applies to generics.)The advantages of genericsThe ability to write generic classes, methods, and interfaces makes it possible to write general template classes. However, they significantly differ from C++ templates, as you’ll see. Such template classes can be reused for multiple purposes without requiring general Object parameters that must be cast to the appropriate data type when the class is used. For example, writing a generic data pool would make it possible to cache and deliver objects of type “X” to an application. One application could then create a pool of Car objects, while another application could create a pool of network Connectionobjects. Both applications would use the generic data pool facility. Neither application would have to write a data pool library, but both would be able to access Carobjects or Connection objects, as appropriate, as though they had written a custom library.Prior to generics, the only way to write a generic data pool was to return objects of type Object. But that return value forces the application to cast the object to the real type, with code like this:Car c = (Car) dataPool.get();Such casts have two problems. First, they interfere with readability — especially if you want to invoke a method on the returned object, because you wind up with a mess like this: Car myCar = ((Car) dataPool.get()).initialize(make, model);With generics, that code turns into something more readable:Car myCar = dataPool.get().initialize(make, model);But the second — and larger — issue with casts is that bugs in programs that use them show up as runtime errors. As Gilad said, a cast amounts to a declaration in which “we hope and pray” the variable contains an object of the indicated type at runtime. If it doesn’t, the app is hosed.On the other hand, with generics, the data pool’s contents are documented, both for the human reader and the compiler. Because the compiler knows what the pool is supposed to contain, errors are discovered at compile time instead of runtime. That shift speeds application development and improves confidence in the final product. How generics workThe declaration for a generic class, method, or interface uses angle brackets to indicate the generic part of the declaration:interface List<E> { ... }The above code defines an interface for a generic list class. Typically, single-character names indicate the generic component, but any valid identifier could be used.Within the declaration, the angle brackets are no longer needed, so the identifier E would refer to the generic data type within the body of the declaration. For example: interface List<E> { void add(E x); ... }When the generic definition is referenced, the angle-bracket syntax is once again used to indicate the actual data type, like so: List<Integer> myList = new LinkedList<Integer>();(Note: In 1.5, the Collections API will be fully genericized, so you’ll be able to use the code above. On the other hand, the generics facility has been cleverly designed so that it is totally backward compatible. As a result, old code of the form List myList = new LinkedList() will continue to run exactly as it did before. That code will still need to cast the data types, but it will run unchanged against the libraries’ generic version.)The data type named in a usage statement could itself be a generic type too. The declaration for a list of lists, for example, would look like this: List<List<Integer>> myList;(Note: Type aliases are currently being considered as an extension to the language in order to simplify such declarations. But the expert group is looking for a way of doing so that avoids adding (abusable) C-style typedefs to the language.)A generic declaration can also name multiple generic types:interface Function<A, B> { B value(A arg); }The declaration above specifies that a class that implements the generic Function interface will include a value()method that takes an argument of type A, and returns an argument of type B. One important difference between Java generics and C++ templates is that in Java, generic declarations are compiled and type-checked into classes that can be reused directly, instead of expanded into additional classes.That fact has several important advantages for the Java developer:If a generic class is used multiple times in an application, only one copy of it exists, rather than one copy for each data type. Consequently, program size is reduced.When a bug is found and a correction made, there is no need to recreate dependent classes that were expanded from the template — the fix automatically applies to all uses of the generic class.Since the generic code is used as a class, rather than expanded from a source code template, the source code does not need to be present to use the generic code. That means third-party library providers can distribute generified components in the same way that they deliver other classes. (And, since a generic class can be used as though it weren’t generic at all, a developer who is unfamiliar with generics can use the libraries as they normally would.)Compilation also reduces application size since the template isn’t expanded into a different class each time it is used. Finally, since generics are compiled into classfiles, the source code is not required to use a generic library. And since the compiled classes are totally backward compatible, they can be used with code that employs older code like LinkedList()instead of LinkedList<Integer>(). After the presentation, Ed Hansen, a senior application architect for Vision Service Plan and an experienced C++ template developer, had this to say: “It’s a great API. And the fact that generic declarations are compiled and type-checked instead of expanded at runtime (like C++ templates) makes debugging a lot easier.”Using generics todayDespite the fact that generics are not yet a standard part of the Java platform, the generics facility can still be used today! You can download and use the generic compiler with the resulting classes running in Java 1.4.At the moment, the best way to use the generics facility is to create a generic API for your existing library classes. For example, you could add generic declarations to an interface, or you could copy a class, remove the code, and add generics to the resulting stub. You would then compile the generic stub with the generic compiler and import that stub when you compile the code that uses the library (with the generic compiler, once again). That code remains unchanged — it still references List, for example, instead of List<Integer>.When you compile, you turn on unchecked warnings, which warn you when a cast may not be valid — a warning you cannot get today in any other way. However, you’ll receive many meaningless warnings as well. It remains to be seen how easily you can filter out the warnings you don’t care about so you can find the potential bugs in your app.When you run the program, you run with the original classes — the nongeneric versions. That way, you use the stable 1.4 versions at runtime, but you will have had the advantage of the additional compile-time error detection. Since the 1.4 compiler is essentially identical to the generic compiler, you shouldn’t have to recompile the code that uses the libraries. The resulting classes should be identical with either compiler. You might try a binary diff, to be sure. If you’re the adventurous type, you can try creating a totally generic version of your library classes and compile that. The generic compiler can then type-check the implementation for consistency.However, keep in mind that generic classes produced by the generic compiler have not been subjected to the same rigorous testing as classfiles produced by the production compiler. As a result, they could still harbor errors; treat this process as an experiment in progress.Do it for a stable library that won’t be seeing much change and see how the classes run. If they perform, great; if not, drop back to the production compiler, report the bug, and wait for a new version of the generic compiler.AssertionsUnlike generics, assertions are ready in Java 1.4. Assertions, according to Sun’s Joshua Bloch, are “statements the programmer believes to be true” at any given point in the program. The assertion facility adds a new keyword to the language, assert, which captures those assumptions. Adding assertions to a program documents what you believe to be true, making your assumptions clear to others. More importantly, assertion statements let the runtime engine (the VM) verifyyour assumptions.The basic syntax for an assert statement is:assert expression ;Assertions speed up debugging because they immediately identify the differences between your expectations and reality. You don’t have to backtrack from the point of the error to identify the cause.You can also add information to the message generated when an assertion fails, like this: assert expression : data ;For example, if an index’s value is calculated from some base value, then the following statement would validate the index and also indicate the value that caused the problem:assert index > 0 : baseValue ;To enable assertions, you compile with the -source 1.4 switch, which is necessary for backward compatibility. It alerts the compiler that assert is a reserved keyword. Without it, a class is free to use the word assertfor a variable or method name. (However, going forward, that is clearly not a good idea!)Assertions are then compiled into the classfiles and enabled at runtime with the -ea (enable asserts) switch (for assertions in your code) or the -esa (enable system asserts) switch (for assertions in the runtime libraries). Or you can use both switches for the maximum runtime checking of assumptions.The great news is that you can enable assertions in the field. You’ll test with them enabled, of course, to speed the debugging process. And for maximum speed, normal production runs will disable the assertions. But when something goes wrong, you can enable them with a runtime switch to help isolate the error.Bloch advised that you monitor your internal dialogue to write assertions. Every time you think to yourself, “At this point, I know that…” or write a comment to that effect, capture that assumption with an assertion. He mentioned four cases where asserts are particularly helpful:Expected condition asserts:When the execution of the final else clause in an else–if chain should imply that some condition is true, capture that assumption with an assert. If the else clause executes for any other reason, the error will be detected immediately. As a simple example:if (x < 2) { ... } else /* x < 5 */ { .... }Capturing the assumption as an assertion lets you verify that xis indeed less than 5 when the else clause executes.Switch statement asserts:For a switch statement that has no default clause, add a default clause with an assert statement that guarantees failure: assert false : switch-value;That way, the switch statement won’t be silently bypassed in the event of an unexpected value, but will instead generate an error.Control flow asserts:If a loop is always expected to find an entry and return, then code after the loop should never be reached. If an entry were not found for any reason, though, then code after the loop could be reached, and that would represent an error. Adding assert false;after the loop would signal that error, should it ever occur.Lock acquisition asserts:When a hidden method in a thread assumes that some visible method has already acquired a lock, an assert statement can verify that the assumption holds true. A new method — assert Thread.holdsLock(this);— has been added to the Thread class for that very purpose.Bloch also warned against certain uses of asserts.Don’t use them for public preconditions:When the code validates the value of public arguments, it should throw an exception. The exceptions form part of the public contract for the method; the program can trap exceptions, and turning off asserts won’t disable them.Don’t depend on side effects:While an assert may invoke a function, it should only do so to test a value. That function should never perform an operation that the program depends upon, because that operation won’t happen when asserts are disabled.Avoid infinite regress:It’s possible to code an assertion in method A that invokes method B, and code an assertion in method B that invokes method A. Situations like that are clearly unhealthy.Advanced developers can also employ assertions to test invariants, which should not change during the operation of a method. But comparing the final version of the data against the original version requires making a copy, and you only want to do that when assertions are enabled! For that purpose, Bloch presented this idiom, where DataCopy is an inner class:DataCopy copy; class DataCopy { YourDataTypeaCopy; DataCopy() { aCopy = outerClassVariable; } boolean isConsistent() { return aCopy.equals(outerClassVariable); } }At a method’s start, use this statement to make a copy only when asserts are enabled:assert (copy = new DataCopy()) != null ;Finally, at the method’s end (or anywhere inside of it), use this statement to verify that the data has remained invariant:assert copy.isConsistent() ;Templates and patternsTo round out this article, we’ll take a quick look at some of the architectural templates and patterns presented at JavaOne. This year’s conference more heavily represented these subjects — a sign that the language is maturing and being used increasingly in production settings.Effective Java programmingBloch’s talk on effective Java programming (based on his book, Effective Java Programming Language Guide,(Addison Wesley Professional, 2001)) was a sellout. In fact, people were turned away at the door. The talk focused on static factory methods, singletons, utility classes, and type-safe, ordered enumeration classes.It came as a surprise to learn that singleton and enumeration classes could deserialize improperly and that a single-line method could rectify the problem in each case.For singletons, the method is:readresolve() { return INSTANCE; }where INSTANCE is the singleton object.For enumeration classes, the method is:readresolve() { return PRIVATE_VALUES[ordinal]; }where PRIVATE_VALUES is the array of enumeration classes, and ordinal is the numeric value associated with each.But that’s just a fraction of the material presented in the talk, which was a subset of the book. Effective Java Programming Language Guide should be read by all Java programmers — right after they learn the language. For details, see Resources.J2EE BlueprintsThe session on Java 2 Platform, Enterprise Edition (J2EE) Blueprints focused on implementing the Model-View-Controller (MVC) pattern by using the Web tier for presentation (the View), a special Enterprise JavaBean (EJB) to manage access to data (the Controller), and normal business-logic EJBs to manipulate and store data (the Model).In addition to explanations, the J2EE Blueprints Website contains a complete sample application (Java Pet Store), which can be freely used and modified for the initial setup of an e-commerce site. (See Resources for a link to the site.)Common interfacesCommon interface standards benefit everyone. For users, common interface conventions make an application familiar and reduce the learning curve. Developers who adhere to such conventions produce apps that are appealing to users. Adherence to such standards also opens government markets and other markets committed to using only accessible software.Accessible software will make software usable for you when your sight grows dim later in life, or when your repetitive stress injury restricts the devices you can use. Accessibility even allows construction of new I/O devices that aim at accelerating computer interactions — like headset-controlled cursors, for example. Accessible software can take advantage of those devices when they come along, with no additional programming.Jeff Dunn, a Sun staff engineer, pointed out that adding accessibility to an application isn’t hard when the GUI is built on Swing components. Since the components themselves already follow standard keystroke conventions, making apps accessible requires adding text descriptions for images (so text-to-voice processors can read them to the blind), and ensuring that keystrokes, in addition to the mouse, can invoke all operations. Theresa Roberts, senior staff engineer at Sun, described the topics covered by Java Look and Feel Design Guidelines(Sun Microsystems (Addison Wesley Professional, 2001)), which covers accessibility issues. (The book is also available online — see Resources for the URL.) Rather than trying to read all the guidelines, you should know when to consult them for reference. In a nutshell, those topics are:Layout and controls for primary windows — application window and document windowsLayout and controls for secondary windows — dialogs, utility windows, splash screens, and alertsComponents — when to use eachCommon idioms (collections of components) for table managementCommon idioms for adding/removing from listsWizards — how they should look, how they should operateFor convenience, Java Look and Feel Design Guidelines includes a CD-ROM that contains examples, libraries, and localized versions of common words, like File and Save, in various languages. An advanced version of the book is also on the way.ConclusionThe Java platform just keeps getting better and better. The language improvements announced at JavaOne, such as generics and asserts, combined with the increased use of templates, patterns, and guidelines, will only help you write superior code more quickly.Eric Armstronghas been programming and writing professionally since before there were personal computers. His production experience includes artificial intelligence (AI) programs, system libraries, realtime programs, and business applications in a variety of languages. He wrote The JBuilder2 Bible and authored the Java/XML programming tutorial available at http://www.java.sun.com/xml. Java