Why don't java & js allow variable declaration in a condition - java

int a; // why can't I put this in the condition itself
if((a = readData()) > 0){
// do something..
}
I'm wondering why java and javascript don't allow me to declare my variable in the (condition). I understand that it has to do with the fact that a variable declaration has to be the first thing on the line -except for for loops- but what is the underlying reason ? In C++ it seems like they can do it. I don't know C++ so apologizes if I misunderstood.

The reason is simplicity of scope rules. If you allowed variable declaration within a condition, you'd have to define the scope of such a variable, and what it means for a declaration to have a value. Neither is straightforward. Requiring variables in an expression to be already defined is simple, and has no downside. Allowing that is complicated, and would obfuscate the source. That is a posteriori reasoning, of course. It might just be that the language designers had other reasons in the moment.
Why do you care? Bottom line: because the JLS says so.

According to the Java Language Specification (https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.9), an if statement has the following form:
if ( Expression ) Statement [else ...]
A variable assignment, aka an AssignmentExpression, is just one of many sub-types of the abstract Expression (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.26), so you can use its result (which happens to be the value that was assigned) in your if statement.

Its simpel:
A If-statement ist defined as:
if (Boolean) {...
We can now substitute:
if (var1 operator var2){... // with "_ operator _" => Boolean
If we now put i=1 as var1 we would get:
if ((i=1) operator var2){... // compile error: i cannot resolved to a local variable
or int i = 1:
if ((int i = 1) operator var2){...
The obvious problem is: int i = 1 is a type declaration and not a variable and we learned above thet the if clause only accept Boolean variables or expression which lead to a Boolean vaiable...!

I wasn't satisfied with the answer here, so like any normal person would do, I learned java byte code. Well, sort of... the rudimentary. So if anyone read this keep in mind that my findings have some extrapolations in them and could be inaccurate.. However it makes sens why it doesn't happen java (much less in javascript where my head was at when asking this) and it is ho so simple.
So now the byte code part:
here is what I got for this:
int a = 9;
System.out.println(a); // just here to prevent some optimization
if((a = 18) > 15){
System.out.println(a);
}
Code:
0: bipush 9
2: istore_1
// -- removed the System.out.print --
10: bipush 18
12: dup
13: istore_1
14: bipush 15
16: if_icmple 26
Here is what happens:
0: push 9 onto the stack
2: store it in the var a
10: push 18 onto stack
12: duplicate 18 on top of the stack
13: store it in var a
14 : push 15
16: the if statement if value1 is less than or equal to value2, branch to instruction at branchoffset.
So since the content of the condition is evaluated before the if statement itself, if we declared int a in the condition, depending on where we define the scope of int a to be, there would be scoping issues.
If it was inside the if block:
If we suppose int a would be scoped into the if block, it must still be evaluated before reaching it and thus being out of scope !
In other words:
while((int a = 9) > 15)
int a = 9 would be evaluated before reaching the while condition, thus being out of the scope we just defined. We wouldn't enter the while block, but the value of a is well defined! Makes sens why it doesn't happen, doesn't it ?
If we go the other way and says that it is just a shortcut for this:
int a;
while((a = 9) > 15)
Then the language wouldn't be consistent and would bring many confusing code! See the for loop, the variable defined in it is scoped in it not outside for example.
It still doesn't make any sens why it's not possible to do in javascript though. However I don't care enough to find out.
I hope I made it clear, at least it makes some sens to me and I learned something. On a side note, I don't think my question really warranted so much down votes, even tho the answer seems obvious to me now. Not every question on stackoverflow has to be a practical one..

Related

What does just calling .getClass(); do? [duplicate]

This question already has an answer here:
In Java Lambda's why is getClass() called on a captured variable
(1 answer)
Closed 1 year ago.
I'm looking at some decompiled code and seeing .getClass();, i.e. nothing being done with its return value.
public String forLocale(Locale locale, String keyword) {
Stream var10000 = getLocaleMappingList(locale, this.getSupportedLocales());
Map var10001 = this.translations;
var10001.getClass();
Map<String, String> translation = (Map)var10000.map(var10001::get).filter((m) -> {
return m.containsKey(keyword);
}).findFirst().orElse(this.translations.get(FALLBACK));
Preconditions.checkState(translation.containsKey(keyword), keyword + " is not a valid translation key");
return (String)translation.get(keyword);
}
What is that for? Was it in the original code like that? (So far I haven't seen an instance of decompiled code not matching up at least line-wise to source code.)
It kind of looks like an assertion, but then what is achieved by doing that as opposed to letting things go wrong at var10001::get? Or is it more about performance?
Update
Here's the bytecode. Cool thing to learn how to do!
// access flags 0x1
public forLocale(Ljava/util/Locale;Ljava/lang/String;)Ljava/lang/String;
L0
LINENUMBER 184 L0
ALOAD 1
ALOAD 0
INVOKEVIRTUAL com/spotify/i18n/Translations.getSupportedLocales ()Ljava/util/Set;
INVOKESTATIC com/spotify/i18n/Translations.getLocaleMappingList (Ljava/util/Locale;Ljava/util/Collection;)Ljava/util/stream/Stream;
ALOAD 0
GETFIELD com/spotify/i18n/Translations.translations : Ljava/util/Map;
DUP
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
POP
INVOKEDYNAMIC apply(Ljava/util/Map;)Ljava/util/function/Function; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)Ljava/lang/Object;,
// handle kind 0x9 : INVOKEINTERFACE
java/util/Map.get(Ljava/lang/Object;)Ljava/lang/Object; itf,
(Ljava/util/Locale;)Ljava/util/Map;
]
This looks like decompiled code, and my guess is that the decompiler hasn't generated Java code that is equivalent to the original source code.
The literal meaning of
var10001.getClass();
is to return the Class object for the type of the object that var10001 refers to. But the value that is returned appears to be discarded, so the call (apparently) doesn't achieve anything. Hence, my tentative conclusion that the decompiler has stuffed up.
You may need to read the (disassembled) bytecodes directly to discern what they are actually doing. (Or you could try a different decompiler.)
UPDATE
It is plausible that getClass() is called solely for the side-effect of checking for null. (I've never seen that idiom ... but it would work.) I wouldn't expect it to make the code faster, but it would make it more compact.
However, if this is being done in the (original) source code, it would appear to be unnecessary. A couple of lines later, the code takes var10001::get and passes it as an argument in a Stream.map call. I'm pretty sure that that evaluating var10001::get will entail checking that var10001 is not null.

Java converting int variable to String by adding an empty string?

The second argument of the addFormDataPart call should be a string.
int privacyLevel = 0;
builder.addFormDataPart("privacy",privacyLevel);
However, since privacyLevel is an integer, it needs to be converted to String.
In the tutorial, he made the conversion by adding an empty String at the end of privacyLevel:
builder.addFormDataPart("privacy",privacyLevel+"");
I found this to be an unusual way of converting an int to string. And all the research I've done pointed me to traditional methods of conversion.
So does anyone have an explanation for this?
And is this even a "good practice"?
You can also use the proper tool: Integer.toString(int): this might be easier to read as it clearly show your intent on getting a String out of your int.
int privacyLevel = 0;
builder.addFormDataPart("privacy", Integer.toString(privacyLevel));
When you use:
builder.addFormDataPart("privacy", "" + privacyLevel);
You are creating a new String from "" and Integer.toString(privacyLevel), but there might be some optimization during compile time or at runtime:
Compiler can detect the "" and directly use Integer.toString(privacyLevel).
Compiler may replace the + operation by the String::concat method. Said method may return the other String when itself is empty, or return itself when the other String is empty (in openjdk 8u232, the method is actually checking the length of other and always perform a concatenation).
Now, to see what the compiler does, here is a simple code which does nothing extraordinary:
class Foobar {
public static void main(String[] args) {
int n = 1;
String s = n + "";
}
}
If you call javap -p Foobar.class, you can see what the compiler did (with Java 11.0.6, Java 8 use a StringBuilder):
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=1
0: iconst_1
1: istore_1
2: iload_1
3: invokedynamic #2, 0 // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
8: astore_2
9: return
LineNumberTable:
line 8: 0
line 9: 2
line 10: 9
Using Integer.toString only change the invokedynamic to invokestatic:
3: invokestatic #2 // Method java/lang/Integer.toString:(I)Ljava/lang/String
The compiler does not try to be intelligent in this case and don't optimize "" + int.
If you read Javadoc of java.lang.invoke.StringConcatFactory.makeConcatWithConstants(Lookup, String, MethodType, String, Object...), it references JLS 15.18.1 and JLS 5.1.11.
The JLS 15.18.1 explains the concatenation operation from String + Not a String:
If only one operand expression is of type String, then string
conversion (§5.1.11) is performed on the other operand to produce a
string at run time.
The JLS 5.1.11 explains how to convert an int to String:
A value x of primitive type T is first converted to a reference value
as if by giving it as an argument to an appropriate class instance
creation expression (§15.9):
If T is byte, short, or int, then use new Integer(x).
To answer your question:
I found this to be an unusual way of converting an int to string. And all the research
I've done pointed me to traditional methods of conversion.
So does anyone have an explanation for this? And is this even a "good practice"?
What's unusual in your case if the fact the String is after the number, rather than before (as in "privacyLevel: " + privacyLevel), but that is not strange given the JLS 15.18.1.
I could not say it is a good practice, even though it is relative, but I'd say it is legacy practice born for the fact that Integer.toString is not so old (I can't remember if it was added in Java 5 or 6) and that the easier way was to doing n + "" or "" + n.
I would personally favor Integer.toString because I find n + "" rather ugly. It might also be better in terms of performance due to the complexity of the conversion done otherwise. Luckily for you or my answer, someone did a JMH benchmark.
Last but not least, if you stick with concatenation, the result will depends on the version of compiler which will do the actual transformation:
You are using Android Studio, and what apply to vanilla Java may not completely apply here.
Java 8 use a StringBuilder to generate an int' String.
Java 11 use invokedynamic and a lot of stuff harder (for me) to explain, but I assume it does its job and perform as well, if better, than StringBuilder.
Integer.toString seems to be optimized in newer hotspot JVM (it is annotated #HotSpotIntrinsicCandidate in Java 13).

Detect recursive method calls at run time in byte code using ASM (5.x): howto?

The problem is as follows; the method, in Java code, is:
Rule foo()
{
return sequence(foo(), x());
}
This will provoke a parsing loop which of course should be avoided; however, this is legal:
Rule foo()
{
return sequence(x(), foo());
}
Now, somewhere else in the code I do have access to a RuleMethod, which is a class extending MethodNode, and therefore I do have access to the following information:
ruleMethod.name: foo; (defined in MethodNode)
ruleMethod.desc: ()Lorg/parboiled/Rule; (defined in MethodNode)
ruleMethod.ownerClass: com.github.fge.grappa.experiments.SelfReferringRule.MyParser (defined in RuleMethod
And the bytecode of the first code extract above is as follows:
Method 'foo':
0 L0
1 ALOAD 0
2 ALOAD 0
3 INVOKEVIRTUAL com/github/fge/grappa/experiments/SelfReferringRule$MyParser.foo ()Lorg/parboiled/Rule;
4 ALOAD 0
5 INVOKEVIRTUAL com/github/fge/grappa/experiments/SelfReferringRule$MyParser.x ()Lorg/parboiled/Rule;
6 ICONST_0
7 ANEWARRAY java/lang/Object
8 INVOKEVIRTUAL com/github/fge/grappa/experiments/SelfReferringRule$MyParser.sequence (Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Lorg/parboiled/Rule;
9 ARETURN
10 L1
Which means I have each and every information available to me to be able to spot, at least in the bytecode above, that foo() is the first argument of the sequence() invocation, since the constructor accepts three arguments and there are three elements on the stack.
But of course I can't "eye inspect" at runtime. Therefore I need a way to do this...
It looks like what I need is a MethodVisitor and somewhat visitInsn(), then see what arguments there are and detect appropriately...
But I don't have the slightest idea where to start; searching around on the net seems to only give examples of how to modify byte code, not detect such situations :/
Where do I start?
Analysis is generally much easier using the tree api as it allows you to easily backtrack and provides support for flow analysis.
If I understand your problem correctly, all you need to do (if all you wish to support is simple cases such as your example) is scan backwards from the call to sequence. As you know the code compiles what's on the stack must be valid, so just count back three method calls / field gets / etc.
If you want to support more complex scenarios where the inputs are assigned to variables by branch statements you will need some sort of flow analysis.
Create a MethodVistor and while you are in visitCode() of the method foo() look for visitMethodInsn() and if the name argument in the visitMethodInsn() is foo you know that you have a recursive call to the method.
In your bytecode listing you have three INVOKEVIRTUAL instructions, these instructions are visited in order by the visitMethodInsn() function. If you want to check the sequence you can keep track of the order in which the method calls are made. You will see foo() first followed by x() and then finally sequence().
3 INVOKEVIRTUAL com/github/fge/grappa/experiments/SelfReferringRule$MyParser.foo ()Lorg/parboiled/Rule;
4 ALOAD 0
5 INVOKEVIRTUAL com/github/fge/grappa/experiments/SelfReferringRule$MyParser.x ()Lorg/parboiled/Rule;
6 ICONST_0
7 ANEWARRAY java/lang/Object
8 INVOKEVIRTUAL com/github/fge/grappa/experiments/SelfReferringRule$MyParser.sequence (Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Lorg/parboiled/Rule;
If I get your intention correctly, you want to detect direct left recursion, which parboiled cannot handle. However, left recursion can be handled:
In essence, the parser must detect a left recursion and fail there, but remember that a left recursion occured. When the rule which was used in a recursive way succeeds, the result is saved as "seed" and the parsing process is restarted at the original input position. This time, when the left recursion happens, the seed is used instead of failing. This process is repeated.
For explanation see http://www.vpri.org/pdf/tr2007002_packrat.pdf for the paper. The algorithm can easily be adapted to PEG parsers.
A parsing library similar to parboiled using this technique is https://github.com/ruediste/lambda-peg-parser

Why isn't the scope of a switch statement in Java limited?

Why, in Java, is a variable's scope confined to a switch block as opposed to a case block. For example,
// Scope limited to a switch block
switch (number) {
case 1:
String result = "...";
break;
case 2:
result = "...";
break;
In the above example, result needs only to be declared once. If you declare it twice then you receive a Duplicate local variable message.
My question is: how does the program know you've declared result if number = 2?
(It won't fall into case 1 and won't declare the variable... or will it?)
EDIT:
I might be confusing everyone. I understand how I can limit the scope of a variable but my question is: how does Java know that result has been declared if it doesn't fall into the case?
EDIT: Java uses lexical scoping (also called static scoping), so the scope of the variables are determined during compile time, and have nothing to do with the actual evaluation.
Java is block scoped, so it's scope will respect the {} in the example above.
See JLS 6.3:
The scope of a local variable declaration in a block (§14.4) is the
rest of the block in which the declaration appears, starting with its
own initializer and including any further declarators to the right in
the local variable declaration statement.
You can limit the scope to case blocks by adding curly brackets like this:
// Scope limited to a switch block
switch (number) {
case 1:{
String result = "...";
break;
}
case 2:{
String result = "...";
break;
}
In the grandfather language Fortran, there is a computed GOTO statement
GOTO expr
...
1 ...
...
2 ...
...
based on the value of expr, the code jumps to 1, 2 etc.
C's (and Java's) switch statement is basically a computed GOTO in disguise. We have a continuous piece of code with some labels, and we jump to one of the label. If there's no break we'll execute the rest of the block.
This rather low level control mechanism is contrary to the intuitions of today's programmers; we would think that the switch statement selects one clause and executes that clause, much like an if-elseif-elseif-...-else statement.
Java inherited C's switch semantics since they didn't want to deviate from C too much. Newer languages are unlikely to continue the mistake.
Java uses block scoping, select cases aren't blocks (they are more like labels). This would work though:
switch (key) {
case 1: {
String result = "1";
return result ;
}
case 2: {
String result = "2";
return result ;
}
}
What do you mean how? In order to understand the answer you need to learn how compilers work.
Think of the switch as a large block with multiple "goto" statements at the end of each switch statement.
I'm not sure how java unroll switch statements but one easy and simple way to do it is this (pseudo byte-code):
if (number == 1) goto label1;
if (number == 2) goto label2;
goto labelEnd;
label1:
String result = "...";
goto labelEnd;
label2:
result = "...";
goto labelEnd;
labelEnd:
<some code>
The reason a variable scope is confined to a switch block as opposed to a case block is java allows for fall through from one case block to the next i.e.
switch (number) {
case 1:
.... some code ...
case 2:
.... some more code ...
in this case case if number is 1, both case 1 & case 2 are executed. The break acts like a goto end of select, it does not ever end scope. The variable scope has to be at select level.
As others have stated, use a block to restrict scope i.e.
switch (number) {
case 1:{
String result = "...";
break;
}
case 2:{
String result = "...";
break;
}
Because there isn't a 'case block'.
Everything inside the switch statement is in the same block. You can't declare the same variable twice in the same block.
If you want a 'case block' you have to write the { and } yourself.

disable an intellij compiler error

I'm getting a "Variable TMP_1 might not have been initialized" error. Here's the snippet:
10 case 1000:
11 Double TMP_1 = len(T$);
12 I = 1d;
13 case 1001:
14 if (I.compareTo(TMP_1) > 0) {
The error is being reported on line 14. In my program it isn't possible to get to case 1001 without executing the code block at case 1000. Apparently Intellij can't figure that out. How can I disable this error? I'd rather take my changes with a null pointer exception.
The source code was generated by a compiler I wrote (the source language is an ancient BASIC.) Relocating the assignment on line 11 would be very difficult.
EDIT - See Mechanical snail's explanation below. This isn't a compiler bug at all; this is a simple program bug. The issue is that the way I have simulated BASIC's GOTO statement requires that I leave the switch statement. And when I do the tmp variable goes out of scope.
Final edit - I changed the code generator to remove the TMP variables entirely.
case 2026:
V = (asc(V$)) - (asc(" "));
dataCursor.restore();
for (J = 1d; J <= ((V * 8d) * 10d); J++) {
X = dataCursor.read();
}
Previously the arithmetic in the for loop was being done using tmp variables set before the 2026 label. Now because there aren't any, there's no problem.
The Java compiler isn't smart enough to prove that the variable you're switching on will never be 1001 until after the code that initializes the variable is executed. Remember that Java variable declarations are completely static; by design, Java only allows your variable to be used in ways that make sense, i.e. are initialized before use. And proving that this happens, for general code, is equivalent to solving the halting problem. (For all that the compiler knows, the expression I.compareTo(TMP_1) > 0 could be nonsense, since it refers to a nonexistent variable. (More precisely, the variable is declared in the scope of the switch statement's body, but the code that initializes it would not execute if you skip to the label case 1001:.))
You aren't permitted to turn this error into a warning; that's one of the drawbacks of a static language. In particular, the Java Language Specification, chapter 16 requires:
For every access of a local variable [...] x, x must be definitely assigned before the access, or a compile-time error occurs.
and the variable is not "definitely assigned" (as defined in the spec) before access. IntelliJ compiles your code using a Java compiler (usually javac). Since what you're trying to do is required to be an error by the standard, what you want is impossible (short of editing the compiler, and then it wouldn't be Java anymore).
Workaround
Instead, simply declare your variable in the surrounding scope, and initialize it to a dummy value. For example:
Double TMP_1 = null;
while(exitVar) {
switch(lblToGoTo) {
...
case 1000:
TMP_1 = len(T$);
I = 1d;
case 1001:
if (I.compareTo(TMP_1) > 0) { ... }
...
}
}

Categories