Saturday, April 5, 2008

No Java Switch on Long

Section 14.11 ("The switch Statement") of the Third Edition of the Java Language Specification lists the types that the Java switch statement can support switching on. The Java switch statement supports switching on byte, Byte, char, Character, int, Integer, short, Short, or an Enum. When I was first learning Java, it was somewhat surprising to me that Java supported these integral types (except Enum which was not available at that time!), but did not support long or Long. Switching on non-integral types did not make sense, of course, due to the inability to precisely compare non-integral types. However, it seemed like long should be a valid type to switch on as shown in the next code listing.


package longswitch;

/**
* This example demonstrates the switch statement.
*/
public class SwitchExample
{
public static void main(String[] arguments)
{
final long value = System.currentTimeMillis() % 5;
switch ( value )
{
case 1 : System.err.println("One");
break;
case 2 : System.err.println("Two");
break;
case 3 : System.err.println("Three");
break;
case 4 : System.err.println("Four");
break;
default : System.err.println("Default");
break;
}
}
}


When one tries to compile the above code, an error ("possible loss of precision") like that shown in the next image is encountered.



As the Java Language Specification pointed out, a compiler error did result from using one of the types (long) that was not listed in the specification.

The code below makes the compiler error go away and, in this simple case, there is probably no need to worry about any rounding or truncation that might occur in the conversion of the long to an int.


package longswitch;

/**
* This example demonstrates the switch statement.
*/
public class SwitchExample
{
public static void main(String[] arguments)
{
final long value = System.currentTimeMillis() % 5;
switch ( (new Long(value)).intValue() )
{
case 1 : System.err.println("One");
break;
case 2 : System.err.println("Two");
break;
case 3 : System.err.println("Three");
break;
case 4 : System.err.println("Four");
break;
default : System.err.println("Default");
break;
}
}
}


In practice, we rarely "need" to switch on a long and the ability to switch on an int or Enum are sufficient. In fact, especially before Enum was available, I would have liked the ability to switch on String more than on long. Of course, as I have blogged about before, some people insist you should never use the switch statement. The Scala language provides a switch-like match that does not use or require break statements (which Scala does not support). I think that switch statements can be a red flag indicating possible design problems, but I also reject the argument that they are always bad or always the worst possible approach to a particular problem.

3 comments:

Simmie said...

Actually,

switch ((new Long(value)).intValue()) {

is unnecessary.

Simpler approach is:

switch ((int)value) {

In fact, new Long(v).intValue() will allocate memory on the heap, every time it is run; whereas (int)value doesn't need to.

Keith said...

just my opinion, but here are some articles of best practice:

1. never, *never* truncate a long by casting it to an int.

2. never switch on a long - it doesn't make any sense from a practical standpoint. if your code has listed a case for every value available to a java integer you are in serious trouble ...

Dustin said...

Keith,

I agree wholeheartedly with both of your "articles of best practice."

I also hope that no code base out there needs to switch on anything close to 4,294,967,296 (-Integer.MIN_VALUE + Integer.MAX_VALUE + 1) integer values.

Although I'm not against switch statements in general, I do prefer to use enums when possible with them and I prefer polymorphism when available over switching.

Dustin