Friday, May 2, 2008

@SuppressWarnings values

Meta-data and how to associate it has always been a bit of a confusing topic in Java. For instance, the transient modifier is really a kind of marker annotation, as is the @deprecated javadoc tag compilers are also required to process, as is the marker interface Serializable. Then in JDK 1.5, a dedicated annotation facility was added, probably in light of the success of C#'s attributes. One of the first practical uses of annotations appears to be as a way to suppress compiler warnings.

@SuppressWarnings
This build-in annotation allows the developer to signal a respecting compiler that it should forgo a particular warning. It can be applied in front of a type, a field, a method, a parameter, a constructor as well as a local variable. It is up to the compiler to make sense of whatever you put inside the String, the only value mandated by the JLS is the "unchecked". Compilers as well as IDE's will typically implement each their own set of warning types, the Eclipse IDE defines far more of these than NetBeans does for instance.
Here I will only cover the ones supported by the SUN compiler. To see the supported types, issue a "javac -X" command. On my JDK1.6.0_03 system I get:



-Xlint:{all,cast,deprecation,divzero,empty,unchecked,
fallthrough,path,serial,finally,overrides}




Of those ones, "all", "empty", "path" and "overrides" doesn't seem to have any effect when using the SUN compiler or when used inside NetBeans. The following lists the remaining ones and how to use them.

@SuppressWarnings("deprecation")
Use this when you are deliberately using a deprecated method, realizing full well that your code might one day break, but you do not wish to be notified of this. In the following example, we use getYear of java.util.Date which has been deprecated:


   @SuppressWarnings("deprecation")
   
public void suppressWarningsTest()
   
{
       
Date date = new Date();
       
int year = date.getYear();
    }



Without suppressing the warning, the compiler (if invoked with -Xlint:deprecation) would've complained:


warning: [deprecation] getYear() in java.util.Date has been deprecated
int year = date.getYear();



@SuppressWarnings("unchecked")
Java 1.5 brought a crippled, but mostly working generics facility into play. This of course vastly improves type-safety. However, you often need to mix non-generic and generic types and this usually provoked the compiler into complaining. Take the following example:



   @SuppressWarnings("unchecked")
   
public void uncheckedTest()
   
{
       
List nonGenericStringList = new ArrayList();
       
nonGenericStringList.add("Some string");
       
List<String> genericStringList = (List<String>)nonGenericStringList;
   
}



Without suppresssing the warning, the compiler (if invoked with -Xlint:unchecked) will complain with something like:



warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.List
nonGenericStringList.add("Some string");
warning: [unchecked] unchecked cast
found : java.util.List
required: java.util.List
List genericStringList = (List)nonGenericStringList;




@SuppressWarnings("fallthrough")
Java has always followed the C-style of switch statements, where you need to explicititly break out of a switch unless you wish to simply fall through and execute the code in the case below. This can be dangerous of cousee and errors of this kind can be very hard to track down. In the following example, case 1 lacks a break:



    @SuppressWarnings("fallthrough")
   
public void fallthroughTest(int i)
   
{
       
switch (i)
       
{
           
case 1:
               
// Execute both 1 and 2
           case 2:
               
// Execute only 1
       }
   
}



Without suppresssing the warning, the compiler (if invoked with -Xlint:fallthrough) will complain with something like:



warning: [fallthrough] possible fall-through into case
case 2:




@SuppressWarnings("serial")
To ensure a serialized class is consistent with how the Java runtime perceives this object (based on its class definition), it is important that serialVersionUID be present. In practice, most people rely on the compiler to insert an appropriate UID:



    @SuppressWarnings("serial")
   
class Serialest implements Serializable
   
{
   
}



Without suppressing this warning, the compiler (if invoked with -Xlint:serial) will complain with something along the following lines:



warning: [serial] serializable class SerTest has no definition of serialVersionUID
class SerTest implements Serializable




@SuppressWarnings("finally")
Since try-finally blocks are really glorified goto instructions, the compiler can be instructed to complain if it sees a violation of "good practice" when it comes to execution flow. In the following example we issue a return from within a finally. The compiler allows this, since the JLS specifies a finally clause will always get executed, but realize that any exception thrown in the try may be completely disregarded:



    @SuppressWarnings("finally")
   
public String finallyTest(String str)
   
{
       
try
       
{
           
str+=".";
       
}
       
finally
       
{
           
return str.toUpperCase();
       
}
   
}




Without suppressing this warning, the compiler (if invoked with -Xlint:finally) will complain with something along the following lines:



warning: [finally] finally clause cannot complete normally




@SuppressWarnings("divzero")
This can be used to catch explicit integer division by 0. It should be default in my opinion, since the compiler KNOWS that executing the code will always result in a a java.langArithmeticException. The annotation has no effect on double's or float's, since these can represent infinity:



    @SuppressWarnings("divzero")
   
public void divzeroTest()
   
{
       
long l = 12l/0;
   
}



Without suppressing this warning, the compiler (if invoked with -Xlint:divzero) will complain with:



warning: [divzero] division by zero
long l = 12l/0;




IDE's
I reckon most people do not mess with the compiler and supply -Xlint flags, but use the facilities of their IDE. NetBeans allows you to see all (+more) warnings as hints in the gutter section of the editor. You can tweak this to your liking, by going in the options.




NetBeans supports a few more values, namely "empty-statement" and "cast". But that's nothing compared to Eclipse, which supports "unused", "empty", "UnusedAssignment", "UnusedDeclaration", WeakerAccess", "autoboxing" and many more.

Update
Should you be using NetBeans 6.1 or later, I have created a small plugin to assist in the code completion and documentation of these values. You can read more about that here.

Post a Comment