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.
Related
I have a doubt regarding type conversion between numeric values. Below is a snapshot from the book java complete reference. It says integer literals are successfully converted to byte or short provided the range of the value defined fits into the range of byte/short. However, when I try to run the following program it throws an error. Kindly clarify. Thanks.
book
import java.util.Scanner;
class Test{
public static void main(String args[])
{ int a=10;
byte b=16;
b=a;
System.out.println(b);
}}
/tmp/IQOgFrykLL/Test.java:12: error: incompatible types: possible lossy conversion from int to byte
b=a;
^1 error
It says integer literals are successfully converted to byte or short
b=a doesn't involve an integer literal.
An integer literal is something like 10 or 16: an actual number that appears in the source code.
b=10 would work, because 10 is an int literal.
Actually, it's a bit more general than only working with int literals: you can make this assignment with any compile-time constant value.
For example, if you were to make a final, it would work, because then a would be a compile-time constant value, and essentially could then be replaced by the int literal 10 wherever it is used.
Note that the definition of "compile time constant" is quite strict: it has to be a literal, or a final variable initialized with a compile-time constant value.
Declaring a non-final variable and never changing its value doesn't make it compile-time constant: you have to tell the compiler that its value can't change via final.
Declaring a variable as final doesn't make it a compile-time constant either: the value you assign it has to be a compile-time constant too.
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
I'm upgrading my project to Java 1.7 to 1.8. When I compile my code, it gives a compile error as int cannot cast to long.
Here is the expression which returns a primitive long.
public Long getOrganizationIdByUser(String userId){
Query query = getSession().getNamedQuery("getOrganizationIdByUser");
query.setString("id", userId!=null?_userId.toLowerCase():null);
List<Long> returnList = query.list();
return returnList!=null&&returnList.size()>0?returnList.get(0):0;
}
I've confused why 0 cannot cast to long in java 1.8 since this is a basic concept in java and also it works fine in java 1.7.
Error
error: incompatible types: bad type in conditional expression
[javac] return returnList!=null&&returnList.size()>0?returnList.get(0):0;
[javac]
[javac] int cannot be converted to Long
Numeric conditional expressions are subject to “Binary Numeric Promotion”, which is the reason why the following code can be compiled:
Long long1 = null;
Integer int1 = null;
Long long2 = true? long1: int1;
Since both alternatives have a numeric type, the numeric promotion applies, which will first unbox the values, then apply the widening conversion to the int operand, so the conditional expression has a result type of long. Only after, the boxing to Long happens.
So the code above has the unintuitive behavior of throwing a NullPointerException instead of directly assigning the object found in long1 to long2, due to the unboxing and boxing steps.
The same applies to the combination of Long and int, also when the result of the one operand is a method invocation, e.g.
static Long test() {
return Math.random()<0.5? test(): 0;
}
can be compiled without problems. So what’s the difference? In the example
public Long test() {
List<Long> returnList = new ArrayList<Long>();
return returnList!=null&&!returnList.isEmpty()? returnList.get(0): 0;
}
the method invocation is made on an instance of a generic class.
The specification says:
…
If both the second and the third operand expressions are numeric expressions, the conditional expression is a numeric conditional expression.
For the purpose of classifying a conditional, the following expressions are numeric expressions:
An expression of a standalone form (§15.2) with a type that is convertible to a numeric type (§4.2, §5.1.8).
A parenthesized numeric expression (§15.8.5).
A class instance creation expression (§15.9) for a class that is convertible to a numeric type.
A method invocation expression (§15.12) for which the chosen most specific method (§15.12.2.5) has a return type that is convertible to a numeric type.
A numeric conditional expression
Note that this has been changed compared to Java 7, due to the introduction of the “standalone form” expression, which is the opposite of the “poly expression” which is subject to target typing.
To check whether the method invocation is a stand-alone expression or a poly expression, we have to refer to JLS §15.12:
A method invocation expression is a poly expression if all of the following are true:
The invocation appears in an assignment context or an invocation context (§5.2, §5.3).
If the invocation is qualified (that is, any form of MethodInvocation except for the first), then the invocation elides TypeArguments to the left of the Identifier.
The method to be invoked, as determined by the following subsections, is generic (§8.4.4) and has a return type that mentions at least one of the method's type parameters.
Otherwise, the method invocation expression is a standalone expression.
In our case, the last bullet does not apply. This method is a member of a generic class, but it is itself not generic as it doesn’t declare type parameters, so it’s return type doesn’t refer to a method’s type parameter, as there is none.
In other words, this method invocation is a stand-alone expression, having a numeric return type, so the conditional expression is a numeric conditional expression, and should be subject to Binary Numeric Promotion, just like the other examples.
Note that all recent Java 9 implementation do compile this code, just like the Java 6 and Java 7 implementations do.
Try adding 'L' at the end :
returnList != null && returnList.size()>0 ? returnList.get(0) : 0L;
Or if 'returnList.get(0)' return an int, try:
returnList != null && returnList.size()>0 ? (long)returnList.get(0) : 0L;
I am trying to create a generic method that adds any list of numeric types in java. I would like to be able to use this method to add Integers, Doubles, Longs etc..
The below does not seem to work. I get compilation exceptions in eclipse saying:
The operator + is undefined for the argument type(s) Number, T
What could be the problemo? Thank you in advance.
public static <T extends Number> T sum(ArrayList<T> nums){
Number retVal = 0.0;
for (T val : nums){
retVal = retVal+ val;
}
return (T) retVal;
}
Consider what the Java Language Specification says about the additive + operator
If the type of either operand of a + operator is String, then the
operation is string concatenation.
Otherwise, the type of each of the operands of the + operator must be
a type that is convertible (§5.1.8) to a primitive numeric type, or a
compile-time error occurs.
Number is not a type that is convertible to a primitive. You therefore cannot use the + operator with variables of that type.
Consider using a different type or overloading your methods for each numeric type.
instead of retVal = retVal+ val;
Use like below.
retVal = retVal.doubleValue()+ val.doubleValue();
Consider what you are asking for. You want a collection into which you can place numbers of arbitrary types, and perform operations like sum(). There are eight distinct number subtypes (BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short, not counting the atomic versions).
You will have noticed that java.lang.Number has no arithmetic methods, like add() or multiply(). You can't just go adding up instances of Number subclasses willy-nilly; you must consider each possible pairing, decide the rules for converting one or both to a common type for which addition is defined, and then decide what the result type is.
Then this has to be applied for each number you add to the running total. This is a non-trivial design task.
Luckily you can piggy-back off the design decisions already present for primitive types in the JLS, but it's still going to be a lot of work.
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.