10

My question is in regard to the way Java handles String literals. It's quite clear from the Java Language Specs (JLS) that String literals are being implicitly interned - in other words, objects that are created in the String constant pool part of the heap, in contrast to the heap-based objects created when calling new String("whatever").

What doesn't seem to line up with what the JLS says is that when creating a new String using String concatenation with a casted constant String type, which should be considered as a constant String as per the JLS, apparently the JVM is creating a new String object rather than interning it implicitly. I appreciate any explanation about this particular behaviour and whether or not this is a platform-specific behaviour. I am running on a Mac OSX Snow Leopard.

public class Test
{
    public static void main(String args[])
    {
        /*
            Create a String object on the String constant pool
            using a String literal
        */
        String hello = "hello";
        final String lo = "lo"; // this will be created in the String pool as well
        /*
            Compare the hello variable to a String constant expression
            , that should cause the JVM to implicitly call String.intern()
        */
        System.out.println(hello == ("hel" + lo));// This should print true
        /*
            Here we need to create a String by casting an Object back
            into a String, this will be used later to create a constant
            expression to be compared with the hello variable
        */
        Object object = "lo";
        final String stringObject = (String) object;// as per the JLS, casted String types can be used to form constant expressions
        /*
            Compare with the hello variable
        */
        System.out.println(hello == "hel" + stringObject);// This should print true, but it doesn't :(

    }
}
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
HackerMonkey
  • 443
  • 3
  • 13

2 Answers2

6

Casting to Object is not allowed in a compile time constant expression. The only casts permitted are to String and primitives. JLS (Java SE 7 edition) section 15.28:

> - Casts to primitive types and casts to type String

(There's actually a second reason. object isn't final so cannot possibly by considered a constant variable. "A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable." -- section 4.12.4.)

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • I am casting an Object into a String, so that should form a constant expression, no? – HackerMonkey Mar 26 '12 at 00:44
  • 1
    The implicit cast to Object means that it is no longer constant. – Tom Hawtin - tackline Mar 26 '12 at 00:51
  • @Hasanein, that's not the issue -- it's that the `Object` variable that is assigned to a string that is not a constant. – Louis Wasserman Mar 26 '12 at 00:52
  • Changing object to be final still produces a similar result. Can you give an example of forming a constant string expression using casting to Strings ? – HackerMonkey Mar 26 '12 at 01:14
  • @Hasanein `"hi" == "h"+(String)"i"` – Tom Hawtin - tackline Mar 26 '12 at 01:31
  • Thanks, but casting is not necessary here as you are casting a String literal into an object of String which does have the very same effect if you omit the casting. I am more concerned with having Strings hidden in object instances that need to be casted back to String objects – HackerMonkey Mar 26 '12 at 01:33
  • 1
    @Hasanein I can't see how that can happen inside a compile time constant expression. – Tom Hawtin - tackline Mar 26 '12 at 01:38
  • @TomHawtin thanks a lot, clear now that the only use case for String casting is casting a String literal (or constant object) back to String, any other attempt to cast any other object to String is a runtime operation which invalidates the conditions for a constant expression – HackerMonkey Mar 26 '12 at 02:22
3

Seems like because you reference an object here final String stringObject = (String) object;, this is no longer a 'compile-time' constant, but a 'run-time' constant. The first example from here eludes to it with the part:

String s = "lo";
String str7 = "Hel"+ s;  
String str8 = "He" + "llo"; 
System.out.println("str7 is computed at runtime.");     
System.out.println("str8 is created by using string constant expression.");    
System.out.println("    str7 == str8 is " + (str7 == str8));  
System.out.println("    str7.equals(str8) is " + str7.equals(str8));

The string str7 is computed at runtime, because it references another string that is not a literal, so by that logic I assume despite that face that you make stringObject final, it still references an object, so cannot be computed at compile time.

And from the java lang spec here, it states:

"The string concatenation operator + (§15.18.1) implicitly creates a new String object when the result is not a compile-time constant expression (§15.28). "

I cannot find any examples where a cast can be used, except, for this terrible, terrible example:

System.out.println(hello == "hel" + ( String ) "lo");

Which hardly has any logical use, but maybe the part about a string cast was included because of the above case.

dann.dev
  • 2,406
  • 3
  • 20
  • 32
  • Thanks dann, You are right, I think that the only reason thats mentioned in the specs is to support casting a String back to String which I can't see much practical use to it either. – HackerMonkey Mar 26 '12 at 02:23