Are all compile-time constants inlined?
Asked Answered
S

6

25

Let's say I have a class like this:

class ApplicationDefs{
public static final String configOption1 = "some option";
public static final String configOption2 = "some other option";
public static final String configOption3 = "yet another option";
}

Many of the other classes in my application are using these options. Now, I want to change one of the options alone and deploy just the compiled class. But if these fields are in-lined in the consumer classes this becomes impossible right?

Is there any option to disable the in-lining of compile time constants?

Steer answered 18/12, 2008 at 13:20 Comment(0)
K
29

You can use String.intern() to get the desired effect, but should comment your code, because not many people know about this. i.e.

public static final String configOption1 = "some option".intern();

This will prevent the compile time inline. Since it is referring to the exact same string that the compiler will place in the perm, you aren't creating anything extra.

As an alternative you could always do

public static final String configOption1 = "some option".toString();

which might be easier to read. Either way, since this is a bit odd you should comment the code to inform those maintaining it what you are doing.

Edit: Found another SO link that gives references to the JLS, for more information on this. When to use intern() on String literals

Kwei answered 25/2, 2010 at 22:14 Comment(3)
The code aString.toString() returns aString itself, not a new one, so it is as valid as intern() for the purpose of this question. A fresh String copy would be created by the rarely used constructor new String(aString).Inessential
It is useless to intern() string literals. They are already "interned". See here.Presbyter
@Presbyter - It is not useless in this case because it prevents configOption1, etc. from being in-lined in other classes. Otherwise, if some other class references configOption1 and you then change the constant and do not recompile the other class, the behavior won't be what you expect (or likely want). That's the whole point of OP's question.Hodgepodge
S
10

No, it's part of the JLS, I'm afraid. This is touched upon, briefly, in Java Puzzlers but I don't have my copy to hand.

I guess you might consider having these constants defined in a properties file, and have the class that loads them periodically.

Reference: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#5313

Shanda answered 18/12, 2008 at 13:23 Comment(1)
Yep. If you decompile your code with JAD you'll find that all the compile-time constants have been inlined. I would vote for the properties file or the static methods described.Cherin
S
7

Actually, if you remove the final keyword the constants stop being compile-time constants and then your configuration will work like you want.

However, it is strongly suggested that if this is indeed some sort of configuration you are trying to do, you should move to to a more manageable way than constants in some class file.

Synchronize answered 18/12, 2008 at 13:23 Comment(1)
Actually, if you remove the final keyword the constants stop being constants, they will be variables.Artichoke
S
7

No. You could replace them with a static method call, though, like:

class ApplicationDefs {

    public static String configOption1() { return "some option"; }

}

Granted, it’s not beautiful but it would fulfill your requirement. :)

Sustainer answered 18/12, 2008 at 13:24 Comment(2)
This solution renders behavior identical to the one suggested in my answer. I do not see the point in wrapping the constant in a method.Synchronize
@Yuval - Except that in your suggested answer, the variables can be changed by external classes.Oversubtlety
I
6

You can inhibit inlining by making your constant non-compile time constants...

For instance, null is not a compile time constant. Any expression involving a non-compile time constant is not a compile time constant, although javac may do constant folding within the compilation unit.

public static final String configOption1 = null!=null?"": "some option";
Iphigeniah answered 18/12, 2008 at 14:14 Comment(0)
E
-6

There is nothing here that says these values should be inlined. You are just declaring some public, static members. Those other classes are using the values of these members. No inlining is asked. Even the final keyword

But for performance reasons, some JVMs may inline these values in those other classes. This is an optimization. No optimization should change the behaviour of a program. So if you change the definition of these members, the JVM should un-inline the previous values.

This is why there is no way to turn inlining off. Either the JVM does not inline and there is no problem or if it is inlined, the JVM guarantee the un-inlining.

I am not sure what happens when you import statically this class. I think (not sure) the inlining is performed and may cause the trouble you mention. If that is the case, you could basically delete the static import and you are ok.

Empoison answered 18/12, 2008 at 13:38 Comment(3)
I don't think that's the case: the JLS specifies that compile-time constants are always inlined. Moreover, in the case of String compile-time constants, they are interned and all uses refer to the same instance.Shanda
It would be nice to know where the JLS mandates that a compile-time constant must be inlined at compile-time.Empoison
Fourteen years are quite some time but in case you still wondering whether the inlining behavior has been mandated by the JLS, it has been answered, e.g. in Does the JLS require inlining of final String constants?Inconceivable

© 2022 - 2024 — McMap. All rights reserved.