GitHub LinkedIn Feed

What's so Good About Java 7 Anyway?

Dec 31, 2011
Table of Contents

It is common knowledge that the new major release of the Java Development Kit came out a few months back — JDK 7 is here, ladies and gentlemen! Alright, the news itself is not exactly recent, however since the commercial adoption is traditionally going to range from slow to very slow, it’s unlikely that developers will get to use 7-only features for the next six months at the very least. This gives us plenty of time to go through these new features below, ordered by the convenience they provide (code examples are adapted from Java documentation referenced in “Resources”).

New features

#1: Strings in Switch Statements

The idea is simple: when checking a String object against multiple values, you would normally need to create a daisy chain of if-then-else statements. Consider an example classifying the days of the week.

public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
    String typeOfDay;
    if (dayOfWeekArg.equals("Monday")) {
        typeOfDay = "Start of work week";
    } else if (dayOfWeekArg.equals("Tuesday")
            || dayOfWeekArg.equals("Wednesday")
            || dayOfWeekArg.equals("Thursday")) {
        typeOfDay = "Midweek";
    } else if (dayOfWeekArg.equals("Friday")) {
        typeOfDay = "End of work week";
    } else if (dayOfWeekArg.equals("Saturday")
            || dayOfWeekArg.equals("Sunday")) {
        typeOfDay = "Weekend";
    } else {
        throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
    }
    return typeOfDay;
}

Now that String objects can be used in the expression of a switch statement, the code above becomes:

public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
    String typeOfDay;
    switch (dayOfWeekArg) {
        case "Monday":
            typeOfDay = "Start of work week";
            break;
        case "Tuesday":
        case "Wednesday":
        case "Thursday":
            typeOfDay = "Midweek";
            break;
        case "Friday":
            typeOfDay = "End of work week";
            break;
        case "Saturday":
        case "Sunday":
            typeOfDay = "Weekend";
            break;
        default:
            throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
    }
    return typeOfDay;
}

Behind the scenes, comparison is done in a similar way to the String.equals() method, however the documentation claims that generally a more efficient byte code is generated compared to the usual daisy chain of if-then-else statements.

This may not be the most innovative addition, but a very welcome one that I never understood why it took Sun (and now Oracle) so long to implement, trailing behind many other programming languages.

#2: Type Inference for Generic Instance Creation

When invoking the constructor of a generic (parametrised) class, you would use the same type arguments as in the variable definition:

Map<String, List<String>> myMap = new HashMap<String, List<String>>();

In Java 7 you can simply supply an empty set of arguments (<>) for the instantiating part and the compiler will infer the type arguments from the context. So the HashMap example will look as follows:

Map<String, List<String>> myMap = new HashMap<>();

Unlike instantiating a HashMap with no type arguments (new HashMap()), however, which creates a raw type, the method above does not result in an unchecked conversion warning, so this shortcut is as “proper” as the full way in the first example.

#3: Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking

Previously, when catching exceptions, each specific one had to be caught separately with corresponding code blocks for each one, which is fine if the contents of these code blocks are different. However, if they are not, it becomes a copy-pasting exercise as in the example below.

try {
    // Block
} catch (IOException ex) {
    logger.log(ex);
    throw ex;
} catch (SQLException ex) {
    logger.log(ex); throw ex;
}

This can now be simplified with catching multiple exception types together:

try {
    // Block
} catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}

This is another welcome addition, which lets you catch all known exception types and log them appropriately and only handle unknown ones separately with a generic Exception. (NB: Notice how the Java syntax highlighter that I am using on my blog is having issues with properly highlighting the SQLException — just goes to show in how many unexpected ways new additions to a programming language can came out)

#4: The Try-With-Resources Statement

When performing an operation within a try-catch statement with a variable that is not needed outside of that statement but should be closed regardless of whether or not an exception occurs, that variable can now be a resource for the try block. Consider an example:

static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) {
            br.close();
        }
    }
}

Making the BufferedReader a resource gives the following, more elegant code:

static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    } finally {
        if (br != null) {
            br.close();
        }
    }
}

#5: Binary Literals

Integral data types (byte, short, int, and long) can now be represented as binary literals by prefixing it with 0b or 0B.

// An 8-bit 'byte' value:
byte aByte = (byte) 0b00100001; // A 16-bit 'short' value:
short aShort = (short) 0b1010000101000101; // Some 32-bit 'int' values:
int anInt1 = 0b10100001010001011010000101000101;
int anInt2 = 0b101;
int anInt3 = 0B101; // The B can be upper or lower case. // A 64-bit 'long' value. Note the "L" suffix:
long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;

This does not replace any existing coding practices but rather gives a better visual representation, which, for instance, can help you visualise a bitmap of a smiley face (see the link to understand what I’m on about).

#6: Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods

This last new feature is not really a feature of Java as a programming language for developers but more of an optimisation so no new coding practices will result from this. Instead, this improvement involves compile-time warnings and errors when using non-reifiable formal parameters — parameters for objects not completely available at runtime (such as collections) when their parameters are stripped off at compile-time to ensure binary compatibility with libraries and applications created before generics. In short, this is a more in-depth improvement, so instead of paraphrasing it any further, I invite you to click on the heading of #6 and read about this improvement from the horse’s mouth — from Java documentation.

Incompatibilities

As always, there are some backwards incompatibility dangers to expect from the new version and there is a hefty list of them available from the link in the “Resources”, however from a brief look through them, none are crucial and/or likely to cause too many adaptation pains.

In fact, most of the incompatibilities can be broken down into three categories: exceptions-related changes, hierarchy/precedence edge cases and UI/graphics-related improvements. First two are of improvement and consistency nature and should not result in any drama for careful developers, who are used to putting checks in places where they are due and not making assumptions not implied by the specification. However, if your application uses AWT and/or 2D graphics and there were things that you could not do well before, I encourage you to have a look at the Java SE section of the incompatibilities document.

Resources

  • java