I promised a Java Surprise series. Thanks to my full-time job this promise is not hard to remember: every few days there is a fresh surprise for me ;) . The second surprise in this series is actually one a discovered last summer, so I'm cheating a bit. If you know me in real life, then I've probably already bothered you with this one.
First of all: please take a seat. Are you sitting comfortably? Excellent. Did you know that the syntactical priority of a cast to a primitive type is different from the cast to a reference type? Well, it is. Most likely, you will never encounter this, but it is not hard to find an example that will surprise you.
You are probably familiar with autoboxing in Java 1.5. In short,
autoboxing can convert primitive types (such as int
) to
reference types (such as Integer
) for you if
necessary. Hence, you can assign an int
to an
Integer
and you can also cast an int
to an
Integer
. Some (correct) statements:
Integer x = 3; Integer y = (Integer) 3; int z = (Integer) 3;
I'm going to abuse your familiarity with autoboxing to show how weird
it can be that the priority of primitive casts is different from
reference casts. The following program is a correct program that
includes a (redundant) cast to an int
.
public class JavaSurprise2 { public static void main(String[] ps) { int y = (int) - 2; System.out.println(String.valueOf(y)); } }
Compile and run:
martin@logistico:~/tmp> javac JavaSurprise2.java martin@logistico:~/tmp> java JavaSurprise2 -2
Well, that looks great. Now, let's replace the int
with
an Integer
.
public class JavaSurprise2 { public static void main(String[] ps) { int y = (Integer) - 2; System.out.println(String.valueOf(y)); } }
Compile ...
martin@logistico:~/tmp> javac JavaSurprise2.java JavaSurprise2.java:4: cannot find symbol symbol : variable Integer location: class JavaSurprise2 int y = (Integer) - 2; ^ JavaSurprise2.java:4: illegal start of type int y = (Integer) - 2; ^ 2 errors
What the heck? Cannot find symbol? Let's give it a symbol ...
public class JavaSurprise2 { public static void main(String[] ps) { int Integer = 3; int y = (Integer) - 2; System.out.println(String.valueOf(y)); } }
Compile and run ...
martin@logistico:~/tmp> javac JavaSurprise2.java martin@logistico:~/tmp> java JavaSurprise2 1
So, what happens? Of course the compiler is right. As I said in the
beginning, the priority of a cast to a primitive type is different
from a reference type. Because of the priorities defined in the Java
Language Specification, the int
example is parsed as a
cast. However, the Integer
version is parsed to an
expression name: an expression that can be referred to using a name
(aka variable). The Java compiler will never come back to this
decision to make it a cast anyway: syntactical choices are always
committed.
I can illustrate these different parses using Java-front, a package that provides a Java parser that is generated from a declarative syntax definition for Java in SDF (yes, I'm the developer: marketing intended ;) )
martin@logistico:~/tmp> echo "(Integer) - 2" | parse-java -s Expr Minus(ExprName(Id("Integer")),Lit(Deci("2"))) martin@logistico:~/tmp> echo "(int) - 2" | parse-java -s Expr CastPrim(Int,Minus(Lit(Deci("2"))))
Or in terms of XML:
martin@logistico:~/tmp> echo "(Integer) - 2" | parse-java -s Expr | aterm2xml --implicit <Minus> <ExprName><Id>Integer</Id></ExprName> <Lit><Deci>2</Deci></Lit> </Minus> martin@logistico:~/tmp> echo "(int) - 2" | parse-java -s Expr | aterm2xml --implicit <CastPrim> <Int/> <Minus> <Lit><Deci>2</Deci></Lit> </Minus> </CastPrim>
Surprised? You'd better be!
2 comments:
This on is hilarious :D
Can you say something about the possible reasoning behind this design?
I presented this as being unbelievably weird (and in a sense it is), but there is a good reason for it.
I want to explain that in some more detail, so'll come back to that in my next post.
Post a Comment