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