21
Sat, Dec
3 New Articles

What's New in Java 1.5?

Java
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Java 1.5 (codenamed "Tiger" and also referred to as J2SE 5.0) is more than just your typical Java release. Traditionally, each release of the Java programming language consists of new and deprecated APIs and modifications within the Java Virtual Machine's (JVM) internals (e.g., garbage collection), etc. While the 1.5 release surely has its fair share of such changes, it is also accompanied by a fundamental change in the language itself. That is, the language contains new syntactical structures aimed toward enhanced programmer experience and, even more importantly, more robust applications.

Sun Microsystems first released Java 1.5 in the latter part of 2004, so why discuss the enhanced language features now? Well, if you've been using the Java programming language prior to the 1.5 release, you've probably grown accustomed to what you originally learned; it's now time for you to see how the language has matured and evolved since its inception! Continue reading for a discussion of the language's enhancements and any potential pros and cons that accompany them.

Language Enhancements

The enhancements made to Java beginning with the 1.5 release come in two classifications: either "syntactical sugar" enhancements or application robustness enhancements. That is, some features are geared toward making the programmer's life easier, while others are aimed toward better software engineering practices. The following sections discuss each major language enhancement, state their respective classification(s), and potentially provide an example code snippet of the feature.

Generics

By far, the addition of generics presents the most significant change in the Java programming language. Prior to the availability of generics, when you took an element out of a collection, you had to cast it to the type of the element originally stored in the collection. In addition to the syntactical ugliness of this, it was also prone to runtime errors. Every seasoned Java programmer has (unnecessarily) experienced the elusive ClassCastException during some collection-related operation due to casting an object to the incorrect runtime object type. Generics provide a mechanism for you to declare typed collections for the compiler and let it check your object types at compile time. Using pre-1.5 syntax, here is an example of a method whose job is to determine if a certain customer exists in a list of customers:

public boolean
doesCustomerExistInList(List list, Customer c) {
      for(int i = list.size() – 1; i >= 0; --i) {
            if( ((Customer) list.get(i)).equals( c ))
                  return true;
      }
      return false;
}

The previous code snippet contains a cast to Customer within the for loop. While this clutters the code, it's a language necessity. It's also possible that the list contains objects that are not customers, which would cause a ClassCastException to surface at runtime, likely causing a fatal application error. Using generics, here's the same example revisited:

public boolean
doesCustomerExistInList(List list, Customer c) {
      for(int i = list.size() - 1; i >= 0; --i) {
            if(list.get(i).equals( c ))
                  return true;
      }
      return false;
}

This snippet has two primary differences. First of all, the method's prototype accepts List, which tells the compiler to accept only a list of customers. This prototype guarantees that any caller of our method will pass a list containing only customers; anything else will generate a compile-time error. It's worth noting that whenever the text is encountered within code or APIs, it effectively reads "of type T." For example, List cats says that cats is a list of Cat objects. Having said that, we can also see that the list in the above code snippet is by definition a list of customers, and as such, the cast to a customer object is no longer necessary. By employing the use of generics, we have made our code both more robust and readable.

The generics feature has been given mixed reviews from the field. Many developers argue that it feels awkward to write declarations like Map = new HashMap(); and that they've made the Java APIs harder to comprehend. While they do certainly take some getting used to (just as any other large change in life does), I firmly believe they're for the better. Besides, you're never forced to use generics (though the compiler will rightfully generate type-safety warnings), so you can continue in your old habits if you so choose. While writing your own generified libraries requires some expertise, actually using generified libraries is quite simple and intuitive after you get past the syntax.

There is much more to generics that is beyond the scope of this article; for the full generics overview, including information on writing your own generified libraries, please refer to an excellent overview of generics written by its lead designer, Gilad Bracha.

Enhanced for Loop

If you have been working with Java for some time, one of your pet peeves is surely the "ugliness" of iterating over a collection of items. Take the following example, which iterates over a set of users (which at least can capitalize on generics to eliminate some of the drudgery) and deletes them:

public void deleteUsers(List users) {
      for(int i = 0; i < users.size(); ++i)
            users.get(i).deleteUser();
}

Using the enhanced for loop (also referred to as the for-each loop), the above example can now be rewritten as such:

public void deleteUsers(List users) {
      for(User user : users)
            user.deleteUser();
}

Much cleaner, isn't it? The loop logic, in everyday language, reads "For each User user in users, do the following..." As you can see, the use of the for-each loop combined with the elegance of generics increases readability by making the loop's logic much cleaner than in the pre-1.5 days. In those days, you would find yourself trying to digest the loop's stopping condition and trying to match up parentheses in an effort to figure out which object was being typecast to what type. The for-each loop can be used to iterate over any implementation of the Collection interface as well as arrays of any Java type, so its use is available in a wide variety of contexts. Here's an example that sums an array of integers:

public int sum(int[] nums) {
      int sum = 0;

      for(int num : nums)
            sum += num;

      return sum;
}

Again, this demonstrates another very straightforward and easy-to-understand example. So what can't this new loop construct do? The for-each loop can't be used for filtering, nor can you replace elements in a collection as you traverse it. Lastly, it also can't be used to iterate over multiple collections in parallel. Even with these "shortcomings," use of the for-each loop can greatly reduce the complexity of your loop structure in terms of readability. While this enhancement isn't going to buy you much outside of increased readability, it is one feature whose use I strongly encourage exploring.

Autoboxing/Unboxing

If you're not already familiar with this feature, it will surely grow to be one of your most-liked enhancements. Every Java programmer knows that if you wanted to (logically) put an integer primitive into either a collection or a map, you needed to first box the integer in its wrapper class, Integer. Then, when you finally wanted to retrieve the value from the structure and reassign it to an integer primitive, you needed to unbox the Integer by invoking the intValue() method. The new autoboxing and unboxing language enhancement eliminates this convoluted mess and does the work for you. The following example demonstrates the practical use of generics, the for-each loop, and autoboxing:

List custIds = new LinkedList();

public void addCustomer(Customer c) {
      int custId = c.getCustomerId();
      // The following line demonstrates autoboxing
      custIds.add( custId );
}

public boolean customerExists(Customer c) {
      // The loop's logic reads "For each int custId in the List of
      // Integers, do the following..." which shows that unboxing
      // is happening implicitly
      for(int custId : custIds) {
            if(custId == c.getCustomerId())
                  return true;
      }
      return false;
}

As you can see in the addCustomer(...) method, no longer must we wrap the primitive custId in an Integer wrapper object before inserting it into the linked list of Integer wrapper types. Also notice in the customerExists(...) method the absence of any code calling the intValue() method on an Integer object. The elimination of this boilerplate code greatly enhances code readability as the programmer must no longer sift through code that effectively had no logical implications.

The primary advantage of this enhancement is that there is less of a distinction between a primitive integer and its wrapper equivalent. However, there are subtle differences worth noting. First off, an Integer expression can contain a null value (e.g., Integer x = null). If an application tries to unbox a null value, a NullPointerException will be thrown. Also, there is a small degradation in performance when using autoboxing and unboxing, so using it in performance-critical (e.g., in a scientific application) is strongly discouraged.

The Java language designers recommend using this feature when there is an "impedance mismatch" between reference types and primitives (e.g., when inserting numerical values into a collection, as shown in the example above).

Type-safe Enumerations

An enumeration type in Java...all I can say is, "It's about time!" This is one of my personal favorite enhancements of this release. Why type-safe enumerations were not included in the early days of Java is beyond me, but they've finally arrived. In the "old" days, representing an enumerated type looked much like the following:

public static final int DAY_SUNDAY = 0;
public static final int DAY_MONDAY = 1;
...
public static final int DAY_SATURDAY = 6;


While this appears to be all right at a first glance, it has a few fundamental problems in terms of application runtime robustness. Its two most crucial problems are these:

  1. No type-safety—Since each day is simply an integer type, it's impossible for the compiler to check that an argument to a method expecting a "day" is actually going to receive a day. The best a programmer could do is check the parameters' values and generate a runtime error if an acceptable value was not passed, but it's still too late in the game; this is an error that could (and should) have otherwise been caught at compile time.
  2. No namespace—Integer constants must be (or should be anyway) prefixed with a string (e.g., DAY_) to avoid logical collision with other integer types.

Java 1.5 introduces full language support for enumerated types that addresses the problems noted above. Rewriting the above example using the new enumerated types looks like this:

enum Day { SUNDAY, MONDAY, ..., SATURDAY };

To address the type-safety concern, method prototypes can now be declared to accept an argument of type Day, and any attempt to pass anything other than a Day will result in a compile-time error.

The namespace concern is addressed through the enumerated type's name, Day. The Day type can now be used just as any other type; it can even be used in a switch statement. The above example shows Java's enumerated types in their most simplistic (and most commonly used) form; however, they're even more powerful as they can even contain data and operations. For a more comprehensive look at Java's implementation of enumerated types, please have a look at Sun's official enumeration summary.

Varargs

A shortened name for variable arguments, varargs is an enhancement that renders itself virtually useless in most programming scenarios, but I'll mention it for the sake of completeness. In past releases, any method that accepted an arbitrary number of arguments required the creation of an array containing the various values to pass to the method. Now, methods can have prototypes such as the following example:

public static String format(String pattern, Object... arguments);

The three periods after the final parameter's type indicate that the argument can be passed either as an array of arguments or as separate arguments. This implies that varargs may only be used in the final argument position. It's likely the only time you will experience a need to be familiar with the varargs syntax is when using the PrintStream::printf(...) or MessageFormat::format(...) APIs.

Static Import

Prior to Java 1.5, if you wanted to use a constant defined in an interface, you were forced to fully qualify the static member by name. For example, to compute the circumference of a circle, you would write this:

double circumference = 2 * Math.PI * radius;

 

The new static import construct allows you to directly import static members without inheriting from the type containing the static members or fully qualifying their names. Revisiting the example above, you can write this:

import static java.lang.Math.PI;
double circumference = 2 * PI * radius;

Although its use is not typically encouraged, you can statically import all members by using the asterisk qualifier (e.g., import static java.lang.Math.*). The latter example gives the logic enhanced readability because the expression is not bloated with location qualifiers. In practice, the static import feature should be used sparingly. If used too much, it can make your code much more difficult to understand as readers of your code will not automatically know where certain constants are defined and they will be forced to continuously glance at your code's import section. It's excellent for well-known constants (e.g., pi or other globally recognizable constants within your application's team) because the constant's value is implicitly known by the majority of the code's reviewers.

Compatibility Issues

With the drastic changes in syntactical structures available beginning with the Java 1.5 release, it is undoubtedly obvious that there is the potential for JRE compatibility issues if you start adopting 1.5 syntax features within already-existing applications and libraries. For example, if you are writing library code that is shared between disparate applications (e.g., a JDBC driver) and you introduce 1.5 syntactical structures within your code, you implicitly require all consumers of your library code to run within 1.5 (or higher) runtimes. This of course stems from the fact that pre-1.5 runtimes can't understand the byte-code generated by newer compilers. Therefore, you will have to provide your consumers with sufficient information (e.g., "As of version X of library Y, the use of a 1.5 or higher JRE is required.") to update (i.e., switch to a newer runtime) their application(s) to work with your library code.

Are These New Features Worth a Try?

I wouldn't be writing about them if they weren't! All jokes aside, as someone who's been writing Java code for several years, many of these enhancements truly are long-awaited and useful. Will any of these enhancements make your applications run faster? Probably not. However, they will make your applications more robust (e.g., if you use generics, the compiler can catch many common mistakes that don't typically surface until runtime...when it's already too late) and easier for your colleagues to read.

The enhancements presenting the most widely usable situations are generics, autoboxing and unboxing, type-safe enumerations, and the enhanced for loop. As you introduce generics into "legacy" code, you may even be surprised to find that you were putting the wrong object type into a data structure and never knew it! To start taking advantage of the new features, simply download a 1.5 (or later) version of the Java Development Kit (JDK), which is accompanied by a compiler that can parse the new language constructs. I strongly recommend experimenting with these new features and using them as you maintain old code and as you write code in the future. Your life will surely be simplified.

Joe Cropper is a Software Engineer at IBM in Rochester, Minnesota. Joe works as part of the System i Software Final System Test team, and his areas of expertise include database, multi-tiered client-server technologies, and a variety of object-oriented programming languages. Joe can be reached via email at This email address is being protected from spambots. You need JavaScript enabled to view it..

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: