Core Java

Java 7 – Project Coin Decompiled

Hi all, it’s time to start writing in 2012. As you may have seen already in other blogs, there are a few changes that can make your developer life a lot easier when programming in Java: diamond operator, Strings in Switch, try with resources, multi catch, etc.

In this post (PART I) we are going to see some of the small changes presented in Java 7 Project Coin (JSR 334) and then (in PART II) we are going to decompile them in order to see what’s the compiler doing (for educational purposes only).

What you need

Diamond Operator

Generics helped us to reduce ClassCastExceptions among other things, but some times it can make the code hard to read. Diamond operator is a really nice change. Imaging that you need to group customers by city. You would need something like this:

//suppose the classes City and Customer exist
...
Map<City,List<Customer>> map = new
HashMap<City,List<Customer>>();
...

Now, what if you also need to group the data by country:

//suppose the classes Country, City and Customer exist
...
Map<Country,MapltCity,List<Customer>>> map = new HashMapl&t;Country,MapltCity,ListltCustomer>>>();
...

Now, it starts to be very hard to read, right? What if you also need to group by Region?

//suppose the classes Region, Country, City and Customer exist
...
Map<Region,Map<Country,Map<City,List<Customer>>>> map = new HashMap<Region, Map<Country,Map<City,List<Customer>>>>();
...

So what do you think? It’s not easy at all to read code like that. Fortunately, the new Diamond Operator helps a lot in the readability of code. The last code can be recoded in Java 7 like this:

//suppose the classes Region, Country, City and Customer exist
...
Map<Region,Map<Country,Map<City,List<Customer>>>> map = new HashMap<>();
...

A lot better!

Strings in Switch

I’ve been waiting for this a lot!!! I remember the days when I was starting in the Java world and I really needed to use Strings in switch. Well, my  waiting is finally over. In earlier versions of Java you had to write something like this:

//in a class
...
public void stringToNumber(String str)
{
   if(str.equalsIgnoreCase("one"))
   {
      System.out.println(1);
   }
   else if(str.equalsIgnoreCase("two"))
   {
      System.out.println(2);
   }
   else if(str.equalsIgnoreCase("three"))
   {
      System.out.println(3);
   }
}
...

In Java 7 you can write it like this:

//in a class
...
public void stringToNumber(String str)
{
   switch(str)
   {
      case "one":
         System.out.println(1);
         break;
      case "two":   
         System.out.println(2);
         break;   
      case "three":
         System.out.println(3);
         break;
   }
}
...

Even NetBeans has an option to convert it automatically:

Try with resources and Multi Catch

This is a nice enhancement in this version, now you don’t have to worry about closing those ResultSets, Statements, FileInputStreams…etc. You just need to use the new try structure and the compiler will care about it for you. You can even create your own classes to be Closeable (this is a new interface) by the new try structure. Following is a classic file access via streams:

//in a class
import java.io.*;
...
public static void copyFile(String path) 
              throws IOException, NullPointerException 
{
        File file = new File(path);

        FileOutputStream fout = null;
        FileInputStream fin = null;
        try {
            try {
                fout = new FileOutputStream("file.dat");
                fin = new FileInputStream(file);

                byte[] bytes = new byte[1024];
                int leido = 0;
                while ((leido = fin.read(bytes)) != -1) {
                    fout.write(bytes, 0, leido);
                }
            } finally {
                if (fout != null) {
                    fout.close();
                }
                if (fin != null) {
                    fin.close();
                }
            }
        } catch (NullPointerException ex) {
            ex.printStackTrace();
            throw ex;
        }catch (IOException ex) {
            ex.printStackTrace();
            throw ex;
        }
    }
...

If you notice, in order to be sure that the opened Streams are being closed once they finish, you have to code a try/finally block and close them by yourself. In Java 7, you can achieve the same behaviour in a better way and with less lines of code using the new try structure and the new NIO.2 classes:

//in a class
import java.nio.file.*;
import java.io.*;
...
public static void copyFile(String src) 
              throws IOException, NullPointerException 
{
        Path path = FileSystems.getDefault().getPath(src);
        
        try (FileOutputStream fout = new FileOutputStream("file.dat")) {
            Files.copy(path, fout);
        } catch (NullPointerException | IOException ex) {
            ex.printStackTrace();
            throw ex;
        }
}
...

Binary literals and underscores in literals

Now you can express an integer as a binary literal, this is ideal when programming low-level APIs, also you can use underscores in order to make your values more readable:

//in a class
...
public static void coin() 
{
   int binaryNum = 0b10; //This is number 2 in binary code

   double value1 = 1000000000; //hard to read?
   double value2 = 1_000_000_000; //Easy to read with Java 7

   double value3 = 0b101010110111; //hard to read?
   double value4 = 0b1010_1011_0111; //Easy to read with Java 7

   double pi = 3.14_15_92; //another example of readability
}
...

So, less code, more productivity and a better code readability that’s what Project Coin is about! (Among other things not seen here).

Diamond Operator

This is the example of diamond operator we just saw in the last post:

//suppose the classes Region, Country, City and Customer exist
import java.util.*;
...
Map<region,map<country,map<city,list>>> map = new HashMap<>();
...


</region,map<country,map<city,list

Now, let’s see what’s the generated code by the compiler looks like:

import java.util.*;
...
java.util.Map map = new HashMap();
...

Just an old school map definition and instantiation… Why? Because that’s how generics works:

When you take an element out of a Collection, you must cast it to the type of element that is stored in the collection. Besides being inconvenient, this is unsafe. The compiler does not check that your cast is the same as the collection's type, so the cast can fail at run time.

Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection.

It means that the compiler will check during compile time if you are using the correct classes and it will add any necessary cast to the generated class. For example:

//suppose the classes Region, Country, City and Customer exist
import java.util.*;
...
Map<region,map<country,map<city,list>>> map = new HashMap<>();
Map<country,map<city,list>> m = map.get(new Region());
...
</country,map<city,list</region,map<country,map<city,list

You will get something like this:

//suppose the class Region exists
import java.util.*;
...
Map map = new HashMap();
Map m = (Map)map.get(new Region()); //the compiler added the cast
...

Strings in Switch

Remember the Strings in switch example presented in the last post:

//in a class
...
public void stringToNumber(String str)
{
   switch(str)
   {
      case "one":
         System.out.println(1);
         break;
      case "two":   
         System.out.println(2);
         break;   
      case "three":
         System.out.println(3);
         break;
   }
}
...

After decompiled, you will notice how is that Strings are now supported in switch statemens:

//in a class
...
public static void stringInSwitch(String str)
{
        String s = str;
        byte byte0 = -1;
        switch(s.hashCode())
        {
        case 110182: 
            if(s.equals("one"))
                byte0 = 0;
            break;

        case 115276: 
            if(s.equals("two"))
                byte0 = 1;
            break;

        case 110339486: 
            if(s.equals("three"))
                byte0 = 2;
            break;
        }
        switch(byte0)
        {
        case 0: // ''
            System.out.println(1);
            break;

        case 1: // '01'
            System.out.println(2);
            break;

        case 2: // '02'
            System.out.println(3);
            break;
        }
}
...

Yes… it’s a lilttle trick. It’s not like Strings are supported in switch statements directly, but their hashCodes are (hashCodes are integers). Looking at the code, I realize that it’s better not to abuse using Strings in switch statements, because at the end, you get two switch statements…

Try with resources and Multi Catch

Remember the try with resources and multicatch example from the last post:

//in a class
import java.nio.file.*;
import java.io.*;
...
public static void copyFile(String src) 
              throws IOException, NullPointerException 
{
        Path path = FileSystems.getDefault().getPath(src);
        
        try (FileOutputStream fout = 
                        new FileOutputStream("file.dat")) {
            Files.copy(path, fout);
        } catch (NullPointerException | IOException ex) {
            ex.printStackTrace();
            throw ex;
        }
}
...

This example uses try with resources and multicatch all in one example. When I tried to decompile the generated class using JAD Java Decompiler, I got a lot of erros about the nested try statements, therefore I decided to try JD Java Decompiler and here is the result:

//in a class
import java.nio.file.*;
import java.io.*;
...
public static void copyFile(String src) throws IOException, NullPointerException
{
    Path path = 
         FileSystems.getDefault().getPath(src, new String[0]);
    try 
    {
      FileOutputStream fout = new FileOutputStream("file.dat"); 
      Throwable localThrowable2 = null;
      try 
      { 
        Files.copy(path, fout);
      }
      catch (Throwable localThrowable1)
      {
        localThrowable2 = localThrowable1; 
        throw localThrowable1;
      } 
      finally 
      {
        if (fout != null) 
        { //I added this { symbol for readability

          if (localThrowable2 != null) 
          { //I added this { symbol for readability

            try 
            { 
              fout.close(); 
            } 
            catch (Throwable x2) 
            { 
              localThrowable2.addSuppressed(x2); 
            } 

          } //I added this } symbol for readability
          else 
          { //I added this { symbol for readability

              fout.close();  

          } //I added this } symbol for readability

        } //I added this } symbol for readability
      }
    } 
    catch (IOException ex) 
    {
      ex.printStackTrace();
      throw ex;
    }
}
...

From the last piece of code, we can see how the compiler makes sure that any exception thrown during the copy process is not lost using the new (JDK 7) method +addSuppressed(Throwable):void of the class Throwable This is important, because you will need all the possible exceptions when looking for a bug in your application. Also, notice that all the close operations are done in a finally statement ensuring that the resources are always closed at the end of the process.

Binary literals and underscores in literals

I think you can figure out what we’ll get after decompiling this last feature…

//in a class
...
public static void coin() 
{
   int binaryNum = 0b10; //This is number 2 in binary code

   double value = 1000000000; //hard to read?
   double value = 1_000_000_000; //Easy to read with Java 7

   double value = 0b101010110111; //hard to read?
   double value = 0b1010_1011_0111; //Easy to read with Java 7

   double pi = 3.14_15_92; //another example of readability
}
...

Yep, nothing new… the compiler just rewrites the values without the underscores and translates the binary values to integer values:

//in a class
...
public static void coin(
{
        int binaryNum = 2;
        double value1 = 1000000000D;
        double value2 = 1000000000D;
        double value3 = 2743D;
        double value4 = 2743D;
        double pi = 3.1415920000000002D;
}
... 

Ok, that’s all for this post. Hope you all like the new features of Project Coin (JSR334) in Java 7. There are more improvements to come in Project Coin II in Java 8 and we’ll check them all in future posts. see ya!

Reference: Java – Project Coin Decompiled and Java 7 – Project Coin Decompiled Part II from our JCG partner Alexis Lopez at the  Java and ME blog.

Alexis Lopez

Java Lover, certified as Java Programmer, Mobile Application Developer and Web Component Developer.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button