Why isn't null a compile time constant? - java

So if I have a static final Object CONSTANT = null, for some reason if I reference that in another piece of code like doSomething(CONSTANT), it won't be in-lined onto the code during compilation. So instead of being doSomething(null) after being compiled, it would be doSomething(CONSTANT).

Your CONSTANT is not a compile time constant because the JLS says it is not. The only types that can be used in constant expressions are the primitive types and String.
The sense of it is that an Object instance (in general) has a semantically significant object identity that distinguishes it from other Object instances. This Object identity cannot be encoded in a class file ... or at least, it can't be encoded with the current classfile formats. (And if it could, there would be all sorts of other problems ...)
The value null could (in theory) be handled as a special case, except that there is not a great deal of point. Specifically, you can't use null in any of the contexts where a "compile time constant" is required (or advantageous) from the linguistic perspective. For instance:
You can't have null as a case expression.
Since == for reference types is not a constant expression, you can't use it for the Java "conditional compilation" idiom involving an if with a constant expression as a condition. (And besides null == null is not a useful condition ...)
As far as inlining is concerned, while the "constant" cannot be inlined in the bytecodes (because of the JLS rules about what a "constant expression" is), the JIT compiler's optimizer would be permitted to do this, and may actually do it ... if there are tangible performance benefits.
Reference:
JLS 15.28 - Constant Expressions

In your case the CONSTANT is not compile time constant.
Compile-time constant is a constant and its value is known at compile time and
it won’t change further, then the compiler replaces the constant name everywhere
in the code with its value .
Generally a primitive type or a string literal that is declared with final is treated as compile time constant by the compiler.
Example:
final int a=10;
final String constant =”this is compile time const”;
These both are compile time constants we can use Compile-time constant expressions in case labels of switch statements
Example for non compile time constants
final String xyz = new String(”this is not a compile time const");
here xyz string object is not a compile time constant.Because this 'xyz' string object is going to create at run time and here compiler knows about the reference only not the string object.
The same is applicable to your static final Object CONSTANT = null
Accroding to JLS
Type of the null literal is the null type;its value is the null reference.

Related

In Java is boolean cast useless?

I see that the explicit cast syntax for boolean (boolean) is syntactically legal, but I can't think of a use for it. The corresponding Boolean object -- or rather casting back and forth between boolean and Boolean -- is handled by autoboxing. So it seems like a useless language artifact of the compiler. Is there a functional scenario I am missing?
It can make a difference when an overloaded method is called. Since the method to call is determined by the static type(s) of the parameter(s) (See JLS, §15.12.2), casting a Boolean to a boolean or vice-versa can change which method is called:
class Ideone {
public static void main (String[] args) {
final Boolean b = true;
foo((boolean) b); // prints out "primitive"
foo(b); // prints out "wrapper"
}
public static void foo(boolean b) {
System.out.println("primitive");
}
public static void foo(Boolean b) {
System.out.println("wrapper");
}
}
Ideone Demo
Notice that when casting from Boolean to boolean, a NullPointerException may occur when the Boolean has a value of null.
Whether this behaviour is (extensively) used or should be used is another debate, however.
rzwitserloot showed another case with boolean and Object in their answer. While rzwisterloot's case seems similar, the underlying mechanism is different since the downcast from Object to boolean is defined separately in the JLS. Furthermore, it is prone to a ClassCastException (if the Object is not a Boolean) aswell as a NullPointerException (if the Object is null).
Not entirely.
Ordinarily, applying the cast operator with a primitive type serves only one purpose, which is to convert one primitive type to another. boolean is the only primitive type with the property that nothing can be converted to a boolean, and a boolean cannot be converted to anything else.
But, there is a second purpose to these, although it's not exactly spelled out in the JLS and is a dubious code style practice: It effectively causes auto-unboxing to trigger. And it even combines the job of triggering auto-unbox and an ordinary type cast:
Object o = true;
boolean b = (boolean) o;
o = "Hello";
b = (boolean) o;
compiles just fine. It'll cause a ClassCastException on the 4th line, as expected. Without the boolean cast, you'd have to do something like:
boolean b = ((Boolean) o).booleanValue();
which is definitely more wordy. Exactly how often you end up wanting to cast some expression of type Object to boolean - probably that's a very rare occurrence, but it is a real one, and disallowing (boolean) would have made the JLS longer, not shorter (and the javac codebase wouldn't be any shorter either).
As stated in other answers, you can use a cast to boolean to enforce auto-unboxing or to select from overloaded methods. As a special case, calling invokeExact on MethodHandle may require such a cast to select the right method signature, even for the return type.
But there was a time when neither of these features existed, still, a boolean cast was allowed. Adding a special rule for boolean would not make the language simpler. But the implications of allowing this feature had been considered.
When we look into second edition of the language specification:
5.1.1 Identity Conversions
A conversion from a type to that same type is permitted for any type.
This may seem trivial, but it has two practical consequences. First, it is always permitted for an expression to have the desired type to begin with, thus allowing the simply stated rule that every expression is subject to conversion, if only a trivial identity conversion. Second, it implies that it is permitted for a program to include redundant cast operators for the sake of clarity.
The only permitted conversion that involves the type boolean is the identity conversion from boolean to boolean.
So back then, a boolean value could only be cast to boolean, that was even stated explicitly, but was intentionally allowed, e.g. “for the sake of clarity”.
Consider a call like foo(bar()) vs. foo((boolean)bar()).
Or, stringBuffer.append((boolean)someMethod()).
The cast could not alter the behavior of the program but provide additional information to the human reader.
Today’s version still states that identity conversions are allowed, but does not restrict the conversions of boolean to identity conversion, as we now can convert between boolean and reference types.
There’s even a second mentioning in the old version:
15.16 Cast Expressions
A cast expression converts, at run time, a value of one numeric type to a similar value of another numeric type; or confirms, at compile time, that the type of an expression is boolean; or checks, at run time, that a reference value refers to an object whose class is compatible with a specified reference type.
Since conversions between primitive types and reference types were not possible back then, there were three distinct use cases for casts, numeric conversions, reference type changes, or it “confirms, at compile time, that the type of an expression is boolean”.
As that’s the only thing a boolean cast could do in that Java version.

Effectively final vs final - Different behavior

So far I thought that effectively final and final are more or less equivalent and that the JLS would treat them similar if not identical in the actual behavior. Then I found this contrived scenario:
final int a = 97;
System.out.println(true ? a : 'c'); // outputs a
// versus
int a = 97;
System.out.println(true ? a : 'c'); // outputs 97
Apparently, the JLS makes an important difference between the two here and I am not sure why.
I read other threads like
Difference between final and effectively final
Effectively final variable vs final variable
What does a variable being “effectively final” mean?
but they do not go into such detail. After all, on a broader level they appear to be pretty much equivalent. But digging deeper, they apparently differ.
What is causing this behavior, can anyone provide some JLS definitions that explain this?
Edit: I found another related scenario:
final String a = "a";
System.out.println(a + "b" == "ab"); // outputs true
// versus
String a = "a";
System.out.println(a + "b" == "ab"); // outputs false
So the string interning also behaves differently here (I dont want to use this snippet in real code, just curious about the different behavior).
First of all, we are talking about local variables only. Effectively final does not apply to fields. This is important, since the semantics for final fields are very distinct and are subject to heavy compiler optimizations and memory model promises, see $17.5.1 on the semantics of final fields.
On a surface level final and effectively final for local variables are indeed identical. However, the JLS makes a clear distinction between the two which actually has a wide range of effects in special situations like this.
Premise
From JLS§4.12.4 about final variables:
A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.29). Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1), reachability (§14.22), and definite assignment (§16.1.1).
Since int is primitive, the variable a is such a constant variable.
Further, from the same chapter about effectively final:
Certain variables that are not declared final are instead considered effectively final: ...
So from the way this is worded, it is clear that in the other example, a is not considered a constant variable, as it is not final, but only effectively final.
Behavior
Now that we have the distinction, lets lookup what is going on and why the output is different.
You are using the conditional operator ? : here, so we have to check its definition. From JLS§15.25:
There are three kinds of conditional expressions, classified according to the second and third operand expressions: boolean conditional expressions, numeric conditional expressions, and reference conditional expressions.
In this case, we are talking about a numeric conditional expressions, from JLS§15.25.2:
The type of a numeric conditional expression is determined as follows:
And that is the part where the two cases get classified differently.
effectively final
The version that is effectively final is matched by this rule:
Otherwise, general numeric promotion (§5.6) is applied to the second and third operands, and the type of the conditional expression is the promoted type of the second and third operands.
Which is the same behavior as if you would do 5 + 'd', i.e. int + char, which results in int. See JLS§5.6
Numeric promotion determines the promoted type of all the expressions in a numeric context. The promoted type is chosen such that each expression can be converted to the promoted type, and, in the case of an arithmetic operation, the operation is defined for values of the promoted type. The order of expressions in a numeric context is not significant for numeric promotion. The rules are as follows:
[...]
Next, widening primitive conversion (§5.1.2) and narrowing primitive conversion (§5.1.3) are applied to some expressions, according to the following rules:
In a numeric choice context, the following rules apply:
If any expression is of type int and is not a constant expression (§15.29), then the promoted type is int, and other expressions that are not of type int undergo widening primitive conversion to int.
So everything is promoted to int as a is an int already. That explains the output of 97.
final
The version with the final variable is matched by this rule:
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (§15.29) of type int whose value is representable in type T, then the type of the conditional expression is T.
The final variable a is of type int and a constant expression (because it is final). It is representable as char, hence the outcome is of type char. That concludes the output a.
String example
The example with the string equality is based on the same core difference, final variables are treated as constant expression/variable, and effectively final is not.
In Java, string interning is based on constant expressions, hence
"a" + "b" + "c" == "abc"
is true as well (dont use this construct in real code).
See JLS§3.10.5:
Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.29) - are "interned" so as to share unique instances, using the method String.intern (§12.5).
Easy to overlook as it is primarily talking about literals, but it actually applies to constant expressions as well.
Another aspect is that if the variable is declared final in the body of the method it has a different behaviour from a final variable passed as parameter.
public void testFinalParameters(final String a, final String b) {
System.out.println(a + b == "ab");
}
...
testFinalParameters("a", "b"); // Prints false
while
public void testFinalVariable() {
final String a = "a";
final String b = "b";
System.out.println(a + b == "ab"); // Prints true
}
...
testFinalVariable();
it happens because the compiler knows that using final String a = "a" the a variable will always have the "a" value so that a and "a" can be interchanged without problems.
Differently, if a is not defined final or it is defined final but its value is assigned at runtime (as in the example above where final is the a parameter) the compiler doesn't know anything before its use. So the concatenation happens at runtime and a new string is generated, not using the intern pool.
Basically the behaviour is: if the compiler knows that a variable is a constant can use it the same as using the constant.
If the variable is not defined final (or it is final but its value is defined at runtime) there is no reason for the compiler to handle it as a constant also if its value is equal to a constant and its value is never changed.

How to switch over a field of an enum? [duplicate]

The Java language documentation says:
If a primitive type or a string is defined as a constant and the value
is known at compile time, the compiler replaces the constant name
everywhere in the code with its value. This is called a compile-time
constant.
My understanding is if we have a piece of code:
private final int x = 10;
Then, the compiler will replace every occurrence of x in the code with literal 10.
But suppose the constant is initialized at run-time:
private final int x = getX(); // here getX() returns an integer value at run-time.
Will there be any performance drop (howsoever negligible it may be) compared to the compile-time constant?
Another question is whether the below line of code:
private int y = 10; // here y is not final
is treated in same way as compile-time constant by the compiler?
Finally, what I understand from the answers are:
final static means compile-time constant
just final means it's a constant but is initialized at run-time
just static means initialized at run-time
without final is a variable and wouldn't be treated as constant.
Is my understanding correct?
Compile time constant must be:
declared final
primitive or String
initialized within declaration
initialized with constant expression
So private final int x = getX(); is not constant.
To the second question private int y = 10; is not constant (non-final in this case), so optimizer cannot be sure that the value would not change in the future. So it cannot optimize it as good as constant value. The answer is: No, it is not treated the same way as compile time constant.
The JLS makes the following distinctions between final variables and constants:
final variables
A variable can be declared final. A final variable may only be
assigned to once. It is a compile-time error if a final variable is
assigned to unless it is definitely unassigned immediately prior to
the assignment (§16 (Definite Assignment)).
Once a final variable has been assigned, it always contains the same
value. If a final variable holds a reference to an object, then the
state of the object may be changed by operations on the object, but
the variable will always refer to the same object. This applies also
to arrays, because arrays are objects; if a final variable holds a
reference to an array, then the components of the array may be changed
by operations on the array, but the variable will always refer to the
same array.
A blank final is a final variable whose declaration lacks an
initializer.
constants
A constant variable is a final variable of primitive type or type
String that is initialized with a constant expression (§15.28).
From this definition, we can discern that a constant must be:
declared final
of primitive type or type String
initialized within its declaration (not a blank final)
initialized with a constant expression
What about compile-time constants?
The JLS does not contain the phrase compile-time constant. However, programmers often use the terms compile-time constant and constant interchangeably.
If a final variable does not meet the criteria outlined above to be considered a constant, it should technically be referred to as a final variable.
According to JLS, there is no requirement that "constant variable" should be static.
So "constant variable" maybe static or non-static (instance variable).
But JLS imposes some other requirements for a variable to be a "constant variable" (besides being just final):
being only String or primitive
initialized inline only, because it is final, and blank final is not allowed
initialized with "constant expression" = "compile-time constant expression" (see JLS quote below)
4.12.4. final Variables (JLS)
A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.28).
15.28. Constant Expressions
A compile-time constant expression is an expression denoting a value
of primitive type or a String that does not complete abruptly and is
composed using only the following:
Literals of primitive type and literals of type String (§3.10.1,
§3.10.2, §3.10.3, §3.10.4, §3.10.5)
Casts to primitive types and casts to type String (§15.16)
The unary operators +, -, ~, and ! (but not ++ or --) (§15.15.3,
§15.15.4, §15.15.5, §15.15.6)
The multiplicative operators *, /, and % (§15.17)
The additive operators + and - (§15.18)
The shift operators <<, >>, and >>> (§15.19)
The relational operators <, <=, >, and >= (but not instanceof)
(§15.20)
The equality operators == and != (§15.21)
The bitwise and logical operators &, ^, and | (§15.22)
The conditional-and operator && and the conditional-or operator ||
(§15.23, §15.24)
The ternary conditional operator ? : (§15.25)
Parenthesized expressions (§15.8.5) whose contained expression is a
constant expression.
Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).
Qualified names (§6.5.6.2) of the form TypeName . Identifier that
refer to constant variables (§4.12.4).
There might be a really small performance drop on some machines for private final int x = getX(); since that would involve at least one method call (besides the fact that this isn't a compile-time constant) but as you said, it would be negligible so why bother?
As for the second question: y isn't final and thus is not a compile time constant, since it might change at runtime.
The final keyword means that a variable will be initialized once and only once. A real constant need to be declared static as well.
So, none of your examples are treated as constants by the compiler. Nevertheless, the final keyword tells you (and to the compiler) that your variables will be initialized once only (in the constructor or literally).
If you need their values assigned at compile time your fields must be static.
Performance is not really that affected, but have in mind that primitive types are immutable, once you have created one it will hold that value in memory until the garbage collector removes it.
So, if you have a variable y = 1; and then you change it to y = 2; in memory the JVM will have both values, but your variable will "point" to the latter.
private int y = 10; // here y is not final
is treated in same way as compile time constant by the compiler ?
No. This is an instance variable, created, initialized an used at runtime.
Just keep in mind that in the following code, x is not compile time constant:
public static void main(String[] args) {
final int x;
x= 5;
}
private final int x = getX();
Will be called the first time your object is declared. The performance "drop" will depend on getX() but that's not the kind of things to create some bottleneck.
Simply speaking while compilation the compiler replaces the reference with the actual value specified, instead of using the reference parameter.
public static void main(String[] args) {
final int x = 5;
}
ie. while compilation the complier take the initialised value of 5 directly for compliation than using the reference variable 'x';
Please check this explanation

what's the difference between current type and true type?

I am confused what's the difference between current type and true type in java? Are there any examples that can help me to understand?
The typecast is used by the compiler to verify that we are using the types properly. (This is called the "current type".)
The true type is used when the code is run.
I am kind of confused what does it mean.
(The following is deliberately non-rigorous. And it should be noted that your "current type" / "true type" terminology is NOT used in the JLS or the Java Tutorials.)
Here's an example:
Integer i = new Integer(12345);
Object o = i;
Integer i1 = o; // HERE #1
Integer i2 = (Integer) o; // HERE #2
In this code, there is one object created, and its true type is Integer. This never changes. Its true type will always be Integer ... for as long as it exists.
The notion of a current type is actually about what the compiler and runtime system "knowns" about the type of a value. For example.
They know that i will contain a reference to an instance of Integer or some subtype of Integer.
They know that o will contain a reference to an instance of Object or some subtype of Object.
Even though >>we<< know that the reference in o is actually a reference to an Integer, the compiler is not allowed to known this. JLS says so!
At the statement labelled HERE #1, the compiler knows that o has a current type of Object and that has to be assigned to i1 which has a current type of Integer. That won't work because not every instance of "Object or a subtype" is an Integer. (For example a String instance isn't.) Hence the compiler gives you a compilation error.
At HERE #2, we added a type cast. This type cast is telling the runtime system to test that the true type of the object referred to by o is Integer or a subtype. If it is, the the compiler and runtime know that it is safe to assign the reference to i2. If not, an exception will be thrown.
Q: So why is there this distinction between true types and current types?
A: It is to make polymorphism type-safe. To allow you to write code that treats an Integer as an Object ... but not as a String.
Q: But I know that o actually contains an Integer. So why can't the compiler?
A: It is a simple example:
Compilers don't have intelligence. They are not allowed to be intelligent ... because that would imply that all Java compiler are required to be intelligent (for portability), and intelligence would make Java compilation many orders of magnitude slower ...
For sufficiently complicated examples, you won't be able to know.
Computer science theory says that there are examples where it is impossible to know what the true type of an expression will be ... without testing it.

What is the difference between a compile time type vs run time type for any object in Java?

What is the difference between compile time and run time type of any object in Java ? I am reading Effective Java book and Joshua Bloch mentions about compile time type and run time types of array instances in Item 26 many times mainly to describe that suppressing cast warnings can be safe sometimes.
// Appropriate suppression of unchecked warning
public E pop() {
if (size == 0)
throw new EmptyStackException();
// push requires elements to be of type E, so cast is correct
#SuppressWarnings("unchecked") E result = (E) elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
Here the author is talking about these different types of types in context of arrays . But through this question I would like to understand the difference between compile time types vs run time types for any type of object .
Java is a statically typed language, so the compiler will attempt to determine the types of everything and make sure that everything is type safe. Unfortunately static type inference is inherently limited. The compiler has to be conservative, and is also unable to see runtime information. Therefore, it will be unable to prove that certain code is typesafe, even if it really is.
The run time type refers to the actual type of the variable at runtime. As the programmer, you hopefully know this better than the compiler, so you can suppress warnings when you know that it is safe to do so.
For example, consider the following code (which will not compile)
public class typetest{
public static void main(String[] args){
Object x = args;
String[] y = x;
System.out.println(y[0])
}
}
The variable x will always have type String[], but the compiler isn't able to figure this out. Therefore, you need an explicit cast when assigning it to y.
An example
Number x;
if (userInput.equals("integer")) {
x = new Integer(5);
} else {
x = new Float(3.14);
}
There are two types related to x
the type of the name x. In the example, it's Number. This is determined at compile-time and can never change, hence it's the static type
the type of the value x refers to. In the example, it can be Integer or Float, depending on some external condition. The compiler cannot know the type at compilation time. It is determined at runtime (hence dynamic type), and may change multiple times, as long as it's a subclass of the static type.
Java is statically typed. That means that every expression (including variables) in the language has a type that is known at compile time according to the rules of the language. This is known as the static type (what you call "compile-time type"). The types in Java are the primitive types and the reference types.
Also, each object at runtime in Java has a "class" (here, "class" includes the fictitious array "classes"), which is known at runtime. The class of an object is the class that an object was created with.
Part of the confusion comes from the fact that each class (and interface and array type) in Java has a corresponding reference type, with the name of the class (or interface or array type). The value of a reference type is a reference, which can either be null or point to an object. The Java language is designed so that a reference of reference type X, if not null will always point to an object whose class is the class X or a subclass thereof (or for interfaces, whose class implements the interface X).
Note that the runtime class applies objects, but objects are not values in Java. Types, on the other hand, apply to variables and expressions, which are compile-time concepts. A variable or expression can never have the value of an object, because there are no object types; it can have a value of a reference that points to an object.
I think of "compile time type" as ANY type a variable can be shown to have at compile time. That would include the declared class, any superclasses, and any implemented interfaces.
At runtime, a given object only has one lowest-level class; it can legally be cast to or assigned to a variable of that class, but also to any variable of any of its subclasses or implemented interfaces. The compiler will (often, anyway) allow you to cast it to anything, but the runtime will throw an exception if you attempt to assign something that is not legal.
Once an object is assigned to the variable, then the compiler will treat it as though it is of the type of the variable. So another use of "compile time" could be the variable type, and you can get around that at compile time by casting to a different type as long as you know the cast will be legal at runtime.
If I speak of just one type, I think of the 'runtime type' of a variable as the actual bottom (top?) level subclass of the variable; the lowest sub-class to which it could be cast. But I also routinely think of any object as an instantiation of any of its legal types.
Hope that helps.
Java arrays are so called "covariant", which means a String[] is a subtype of Object[], and type rules are checked at COMPILE time.
Java arrays check at RUNTIME if the object (e.g. String, Integer, WhatEver) you would like to store into it, is compatible with the type the array actually created with.
For example:
String[] strings = new String[2];
strings[0] = "I am text";
Object[] objects = strings;
objects[1] = new Date(); // Compiles, but at runtime you get an ArrayStoreException

Categories