Arrays as local variable - do not follow Definite Assignment rule in Lambda - java

Have seen following threads:
Java8 Stream compiler message -- local variable must be final or effectively final
Variable assignment in lambda expression
According to JavaDoc
Any local variable, formal parameter, or exception parameter used but
not declared in a lambda expression must either be declared final or
be effectively final (§4.12.4), or a compile-time error occurs where
the use is attempted.
Any local variable used but not declared in a lambda body must be
definitely assigned (§16 (Definite Assignment)) before the lambda
body, or a compile-time error occurs.
Similar rules on variable use apply in the body of an inner class
(§8.1.3). The restriction to effectively final variables prohibits
access to dynamically-changing local variables, whose capture would
likely introduce concurrency problems. Compared to the final
restriction, it reduces the clerical burden on programmers.
Is arrays an exception to rule 1?
A sample program that validates javadoc:
List<Integer> li = Arrays.asList(1,2,3,45,678);
final int v = 2;
li.stream().filter(e-> e!=v).map(e->e).forEach(System.out::println);
v= 5;
compilation error at line v=5; , obviously
A sample snippet that is violating the final assignment rule:
List<Integer> li = Arrays.asList(1,2,3,45,678);
final int[] v = {2};
li.stream().filter(e-> e!=v[0]).map(e->e).forEach(System.out::println);
v[0]= 5;
Output:
3
45
678
Above snippet is giving no compilation error, am i missing something??

In the first case v is variable of type int, here value of v is 2 and it is final variable. when you try to assign 5 it is giving error because you can't change final variable value(Working as expected).
Comes to second case, v in not a variable of type int, it is an array. In Java arrays are objects, so here v is a reference. Generally a reference refers to an object and contains the address of the object. When your trying to do v[0] = 5 here your changing the value inside the object but not the value of the reference. If you try to do v = new int[1] or v={5} then you will get compilation error

Related

why below code say "variable y might not have been initialized" [duplicate]

class Foo{
public static void main(String args[]){
final int x=101;
int y;
if(x>100){
y=-1;
}
System.out.println(y);
}
}
Java compiler understands the condition of the if statement is always true and therefore y will always be initialized. No compile error, as expected.
class Bar{
public static void main(String args[]){
final int x;
x=101;
int y;
if(x>100){
y=-1;
}
System.out.println(y);
}
}
But when I break the declaration and initialization of x into two lines, the compiler does not seem to get that the condition is always true and y will always be initialized.
final int x;
x=101;
byte b;
b=x;
System.out.println(b);
Same thing happens here and the compiler gives a loss of precision error.
final int x=101;
byte b;
b=x;
System.out.println(b);
Again, the compiler can understand that x is inside the range of b.
As part of aiming for portability, there is a very specific set of rules for what a compiler should accept and what it should reject. Those rules both permit and require only a limited form of flow analysis when determining whether a variable is definitely assigned at its use.
See the Java Language Specification Chapter 16. Definite Assignment
The critical rule is the one in 16.2.7. if Statements, "if (e) S" case. The rule for being definitely assigned expands to:
V is assigned after if (e) S if, and only if, V is assigned after S and V is assigned after e when false.
y is the relevant V. It is unassigned before the if statement. It is indeed assigned after S, y = {y=-1;} but there is nothing making it assigned when x>100 is false.
Thus y is not definitely assigned after the if statement.
A more complete flow analysis would determine that the condition x>100 is always true, but the compiler is required by the JLS to reject the program based on these specific rules.
The final variable is fine. The rule is actually: -
"It is a compile-time error if a final variable is assigned to unless
it is definitely unassigned (§16) immediately prior to the
assignment."
The declaration leaves it definitely unassigned, and even the limited flow analysis can determine that x is still definitely unassigned at the assignment.
It has to do with how the compiler determines if a statement will be executed or not. It is defined in the JLS #16:
Each local variable and every blank final field must have a definitely assigned value when any access of its value occurs.
In your case, the compiler can't determine that y has been definitely assigned and gives you an error. This is because it would need to determine that the condition is always true and that is only possible if the condition in the if is a constant expression.
JLS #15.28 defines 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:
[...]
Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).
The JLS #4.12.4 defines constants variables as:
A variable of primitive type or type String, that is final and initialized with a compile-time constant expression, is called a constant variable.
In your case, final int x = 101; is a constant variable but final int x; x = 101; is not.
What have you done for the variable x in the second code is called blank final variable. If a final variable is not initialized when it is declared, then it is known as a blank final variable.
Many Java developers think that value of a final variable is known in the compile time. This is NOT always true. It is said that value of a blank final variable NOT known at the compile time. Hence your second code will give you a compile error. Compiler can see that you have initialized the final variable x, but compile doesn't know it's value. So compiler can't resolve the if statement. Therefor it thinks that variable y is not initialized.
You can read more about Java final variables here.

Why var in JAVA 10 can not be initialized to null?

I was playing around new features that have been introduced in JAVA 10, where in I found quite interesting fact that you can't declare a variable with null.
As soon as you write below piece of code,
var a = null;
It throws an error :
variable initializer is null
Now, as we all know that we can't declare a primitive type to null, so the below statement doesn't make any sense.
int a = null;
That means, if a developer is initializing a var with null, it definitely wants to put an Object in it, instead of a literal val. If that's the case, my questions here is
Why doesn't compiler consider it to be an Object var and instead
throws an error.
On the other hand, if you write below statement, it works perfectly fine :
var a = (Object)null;
What is the reason to declare a var with null
Consider below case where I want to initialize var and want to use it outside condition blocks:
var a = null;
if(some condition) Initialize with some arguments
else Initialize with some other arguments
//Use a variable here
So, in this case, as we want scope of a to be outside of conditional blocks we have a requirement to intialize it will null outside if block, which is not possible using var.
There are (at least) three possible type inference strategies the compiler could apply to var o = null:
pick Void
pick Object
look for a later initialization and pick that type
All of them are technically feasible, so the question emerges, which one makes the most sense for developers.
Clearly, Void is pretty useless and I would argue that Object is not much more useful, either. While correct, picking either of these types is unlikely to help the developer write better and more readable code.
The last option, looking for an initialization, was not adopted on purpose to avoid so-called action-at-a-distance errors:
var parent = null;
// imagine some recursion or loop structure, so this makes more sense
processNode(parent); // expects a parameter of type `Node`
parent = determineParent(); // returns a parameter of type `Node`
If the compiler inferred Node for parent because determineParent() returns it, this would compile. But the code is fragile because changes to the last line, might lead to a different type chosen in the first line and hence to compile errors on the second line. That's not good!
We're used to the fact that changing a type's declaration can lead to errors down the road but here the change (line 3), its effect (line 1), and consequent error (line 2) can be pretty far apart, this making it much more complicated for developers to understand or, better, predict what happens.
By keeping the type inference rules simple, developers have it easier to form a simple but correct mental model of what's going on.
Addendum
There are doubts whether option 3, inferring the type from a later initialization, is indeed technically feasible. My opinion (that it is) is based on my understanding of JEP 286, specifically:
On the other hand, we could have expanded this feature to include the local equivalent of "blank" finals (i.e., not requiring an initializer, instead relying on definite assignment analysis.) We chose the restriction to "variables with initializers only" because it covers a significant fraction of the candidates while maintaining the simplicity of the feature and reducing "action at a distance" errors.
Similarly, we also could have taken all assignments into account when inferring the type, rather than just the initializer; while this would have further increased the percentage of locals that could exploit this feature, it would also increase the risk of "action at a distance" errors.
What I am asking how can we extend the scope in that case. So for example, if I want to initialize a variablee inside a condition block but want to expand the scope so that the same variable can be used outside the block, what can be done in case of var.
The answer is that you either use:
var a = (RealType) null;
or (to be sensible about it) you use a conventional typed declaration:
RealType a = null;
The var form is just a convenience to avoid having to write a specific type. It simply doesn't work when you initialize with null. Inferring Object as the type of a is not useful behavior in the vast majority of cases.
For example, if (hypothetically) a var could be assigned with a null:
var a = null;
if (something) {
a = someMethodReturningRealType();
}
a.someRealTypeMethod(); // Compilation error ... because the inferred
// type is java.lang.Object. Oops!!
And this is the correct way to write the code:
RealType a = null; // Just use a classic variable declaration
if (something) {
a = someMethodReturningRealType();
}
a.someRealTypeMethod();
My example is just to share where all we may need to declare a var as null. And it seems there is no clear way to do that.
No. You do not need to do it. You want to do it ... but "want" and "need" are not the same thing.
Moreover, this is a very basic Question as if we can do Object x = null; why cant we can do var x = null;
Because the JLS forbids it. That's it. End of story.
If you don't like it, fine. But this is not a discussion forum. And we are not the people who need to be convinced anyway.
One could think the question isn't that far-fetched looking at the handling of genric types.
Let's examine it using List<T> list; as an example. If you do not specify the generic type (List list;) the compiler will handle it with the raw type. You can then add everything to the list - of course null. Applying the concepts of raw type and type erasure to var could allow you to declare var o; and assign later on everything to o. But since using the raw type is discouraged it is obvious that it wasn't choosen how var should work.
Regarding the case List<?> list; and applying it to var o = null; you could think of something like this: ? o. But this leads to nothing. In case of List<?> list; the only option that remains is add(null);. Following the theoretical example and trying to be consistent with present concepts this would mean ? o can only be initialized this way:? o = null;. So your intention to decalre a var and initialize it later would syntactically be possible, but symantically it makes no sense as it would be initialized always the same way with null. So it wouldn't provide any additional value but add complexity.
From an Oracle JDK 10 page:
you cannot just use the var syntax to declare a variable without a
value
You cannot initialise a var variable to null either. Indeed it is not
clear what the type should be as it’s probably intended for late
initialisation.
So basically, you have to be specific as to the datatype you want, the compiler cannot just assume you want Object or any other type.
So the following will all fail compilation:
var x;
var x = null;
var x = () -> {}
When you declare a variable with the keyword var, it's just a shortcut for writing. At the compilation, the keyword will be replace by the type of this variable. In order to know what is the type of the variable, the initialization must be explicit. If you initialise your variable with null, the compiler can't know what is the type of the variable and so can't replace var. So it's forbidden.
You cannot initialize a var variable to null. By assigning null, it is not clear what the type should be, since in Java, any object reference can be null. In the following example, because there is no predefined data type for a null value, the compiler is not able to interpret a type 'test', which would cause a complication error.
var test=null;
//Compilation error because compiler cannot infer type for local variable count since any Java object reference can be null
This is explicitly listed as an illegal initialization of a local variable in JLS Sec 14.4.1:
It is a compile-time error if T is the null type.
...
var g = null; // Illegal: null type
Null is a type in Java:
There is also a special null type, the type of the expression null
However, it is impossible to declare a variable of this type (e.g. you couldn't write null g; or Null g; in a way that would refer to that type), and even if you could, the only possible value it could have would be null:
The null reference is the only possible value of an expression of null type.
So there's no point in allowing you to declare a variable of this type.
From the Java 10 Local Variable Type Inference page:
You cannot initialise a var variable to null either. Indeed it is not clear what the type should be as it’s probably intended for late initialisation.
| Error:
| cannot infer type for local variable x
| (variable initializer is 'null')
| var x = null;
| ^-----------^
null is not a type, so the compiler can't infer the type of the RHS expression.
var is used as a lazy type. When you do var a = new Foo();, the compiler knows that a is of Foo type, this will enable you to call members of Foo using a at compile time.
var a = null; tells the compiler nothing. If the compiler treats a as Object class, then there is no purpose in using var type - you could have easily used Object a = null;.
Update
I think you are thinking this is working like the var in Javascript, which is absolutely wrong.
From one of your comments somewhere else:
What I am asking is at time of compilation, compiler can simply change
the type to Object (Upcast) on the safer side and if in program ,
variable has been initialized by some specific class Object, then
compiler can change it that specific type.
This clearly means that you are looking for a var capability that is just like what the Javascript is doing.
Why is it wrong in Java? Consider this:
var a = null; // Compiler makes 'a' an Object
if (checkSomething()) {
a = new Foo(); // Compiler makes 'a' a Foo
}
else {
a = new Bar(); // Compiler makes 'a' a Bar
}
test(a); // Should this be allowed???
public void test(Foo foo) {}
Should the compiler allow this? No. You will only know what type var is only at runtime. The compiler can never guess what this is at compile-time.
This is why var can never totally replace the traditional way of defining an explicit type. Always remember, var is just a syntactic sugar.

Java8 Stream compiler message -- local variable must be final or effectively final

I have a little problem. When I write this for loop, the variable i in f.getAnswerScore().get(i).... is underlined with error message : - Local variable i defined in an enclosing scope must be final or
effectively final. Does this have something to do with streams? Maybe streams can't be used in loops?
for (int i = 0; i < 10; i++) {
correct = active.stream()
.filter(f -> f.getAnswerScore().get(i).getStatus().equals(AnswerStatus.ANSWERED_CORRECT))
.count();
}
Like anonymous inner classes, lambda expressions can only access local variables if they are final or "effectively final" (Java 8 or higher; not final but never changed once assigned).
This is covered by the JLS, Section 15.27.2:
Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
Any local variable used but not declared in a lambda body must be definitely assigned (§16 (Definite Assignment)) before the lambda body, or a compile-time error occurs.
Similar rules on variable use apply in the body of an inner class (§8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.
Declare a final variable equal to i and use it.
for(int i = 0; i< 10; i++){
final int j = i;
correct = active
.stream()
.filter(f-> f.getAnswerScore().get(j).getStatus().equals(AnswerStatus.ANSWERED_CORRECT))
.count();
}
rgettman's answer is a workaround. You never use correct inside the loop, so why bother assigning to it 10 times rather than running it just once with i = 9?
The answer is that you're probably doing something else with correct that you're not showing us, and a thorough answer to the question would probably require us to know what that is. Because it's quite unlikely that a C-style for loop like you've written is actually the best/most elegant solution to the problem you're trying to solve.
One possibility is to collect all the correct values in another stream:
IntStream.range(0, 10).map(i ->
active
.stream()
.filter(f ->
f.getAnswerScore()
.get(i)
.getStatus()
.equals(AnswerStatus.ANSWERED_CORRECT)
)
.count()
)
Of course, you might want to do something else entirely, there's no way for us to know.

error about assign List.element

List<Integer> = New ArrayList<Integer>
//some other code,make sure that the size of list is bigger than 1
System.out.println(list.get(0).getClass().getName()); // print "System.lang.Integer"
list.get(0) = 1; //this code does't work
Why does list.get(0) = 1 give the following error in IDE(eclipse)?
The left-hand side of an assignment must be a variable.
The type of list.get(0) is Integer, isn't it? Integer test = 1; is correct.
Can somebody explain the difference?
You cannot assign to the result of a method call as if it were an array access expression. The result of the method call list.get(0) is a value, not a variable. This is in contrast to an array access expression, e.g. array[0], which can be treated as a variable and be on the left side of an expression.
The JLS, Section 15.26, backs this up by stating the only things that can be considered a "variable" on the left side of an assignment operator.
The result of the first operand of an assignment operator must be a variable, or a compile-time error occurs.
This operand may be a named variable, such as a local variable or a field of the current object or class, or it may be a computed variable, as can result from a field access (§15.11) or an array access (§15.10.3).
Instead, use the set method.
list.set(0, 1); // index, new value
list.get(0) is not a variable its a constant. So you need to use set() or add function.

Compile-time constants and variables

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

Categories

Resources