Philosophy of variable's scope in a switch statement - java

As answered in this question here, the scope of a variable inside of a case belongs to the entire switch statement itself, not just the case. Therefore, this does not compile (duplicate local variable):
int key = 2;
switch (key) {
case 1:
String str = "1";
return str;
case 2:
String str = "2";
return str;
}
I'm interested in mainly two things...
What's the philosophy, or design principle, behind this behavior? (Maybe I'm even asking for the motivation for the switch statement as a whole?)
How does this happen? How does this code look at the bytecode, or even assembly, level?

For better or worse, the semantics of switch in Java were heavily influenced by the semantics of switch in C. And, while we as programmers tend to think of a case label followed by some statements and a break/continue/return as a logical unit, that's not actually how it works, and no such construct exists at the language level. In a switch, break and continue are just statements, and when you execute a switch, you start at the matching case label and execute the remainder of the block. It just so happens that most of the time, you'll hit a break or continue or return before that happens. (See JLS 14.11.) The key sentence is:
All statements after the matching case label in the switch block, if any, are executed in sequence.
Many people believe (IMO, reasonably so) that the switch statement in Java has its priorities backwards; the language treats fallthrough and other control flow oddities as if they were normal case, and break as the exceptional case. But of course, in real code, it's the other way around. (How did Java acquire these backward priorities? By copying from C.)
The scoping rule for switch statements flows pretty directly from this view of the world; if the body of a switch is an undifferentiated block that happens to be peppered with case labels, of course its one big scope. Never mind that this is not actually what almost all developers want almost all the time.
In addition to confusing scoping and fallthrough-by-default, Among the other things people regret about switch in Java is that it is only a statement, not an expression. See JEP 325, which addresses all of these problems (in a backward-compatible way), which will likely be a preview features in Java 12.

Just put a pair of braces around each case clause:
int key = 2;
switch (key) {
case 1: {
String str = "1";
return str;
} case 2: {
String str = "2";
return str;
}
}
This will compile.

Related

Switch statements with identical control expressions but different case implementations

I have two switch statements that are identical in structure but the cases do different things. How do I possibly combine them? Or are there any better design suggestions instead of using two switch statements?
This is how the first switch statement looks:
switch(var){
case 1:
functionA();
break;
case 2:
functionB();
break;
}
The second looks like this:
switch(var){
case 1:
functionC();
break;
case 2:
functionD();
break;
}
Problem is, functionA() cannot be called at the same time with functionC() and same goes for functionB() and functionD() - they are to be called at different times.
Forgive me if this sounds silly, but is there a way to re-use one switch statement for different situations?
NB: This is not a "how do I do this?" question, but a "how do I do this BETTER question".
In my optionion there is no need to further simplify your switch statements, since they are readable and do different things. Also, in order to simplify you would need to add an additional layer of abstraction, which is not (really) possible here.
The only thing you could abstract here is the var parameter. Altough i would not recommend it, maybe you can try something like this:
private Consumer<Integer> createConsumer(Runnable... actions) {
return i -> {
if (actions.length < i)
throw new IllegalArgumentException();
actions[i].run();
};
}
You can create a consumer, which accepts an arbitary amount of Runnable objects
var consumer = createConsumer(
() -> System.out.println("First"),
() -> System.out.println("Second"),
() -> System.out.println("Third"));
and execute it like
consumer.accept(0);
If you also need a default statement Consumer<Integer> createConsumer(Runnable... actions, Runnable defaultOption) could come to rescue, then the IllegalArgumentException would also be superfluous.
However, this would just be some functional style of switch statement, which could make things even more complicated, whenever the parameter does not start at 0, but some other number (you would need to subtract till 0) or if you switch block has "gaps" bewteen numbers (0, 1, 3, ...).

Using literals in Switch statements rather than variables? Java

What role do literals play in switch statements? Would it be better to use variables instead? Or how about using constants?
Usually you switch on a variable, never a literal. You use case on a literal, like this:
public void doSomething(int x) {
switch(x) {
case 1: System.out.println("It's 1."); break;
default: System.out.println("It's something else."); break;
}
}
Using "switch" on a literal wouldn't really make much sense - a literal can only have one value (whose value is known at the time you write the code). The switch block lets you do different things depending on what that value is. But since you know what the value is (it's a literal) it wouldn't make sense to switch on a literal.
Constants often make an appearance in the case statement, but again not in switch for the same reasons literals aren't used there.

Switch Case statement enum not working?

I have a problem concerning switch/case statements in java in combination with enums.
In my code I want to do something based on the Enum of the type "MatchingMethods" set in the object "currentMethod".
The enum "MatchingMethods" contains several enums in the form
{EXACT_STRING_MATCHING, DEPTH_MATCHING, [...]}
Now the strange thing is though the object "currentMethod" contains an enum of the type "EXACT_STRING_MATCHING" not only the first case is executed but also the second one.
I know there is no break statement after the code of the first case but the code of the second case shouldn't be executed in my opinion because the enum "EXACT_STRING_MATCHING" doesn'T match with "DEPTH_MATCHING".
If I put in a break statement after the first case it seem to be totally fine…
My code is the following:
[...]
MatchingMethods mM = currentMethod.getMatchMethod();
switch (currentMethod.getMatchMethod()) {
case EXACT_STRING_MATCHING:
//do something here
case DEPTH_MATCHING:
comparedNodePair.setDepthMatchResult(currentMetricResult);
break;
[...]
I am totally confused…
May someone be able to help me?
You already mentioned it, you have no break - switch works like goto where the case are labels to be jumped at and no "boundaries" or functions.
This is also the biggest critique concerning switch, because no one would use goto today, but switch which is certainly similar.
But it gets executed, because once one of the case satements is true the flow of execution "falls trough" see here for some information
this is normal if there is no break statement at the end of the case block.
add the break statement is necessary if you only want the exact block to be executed.

Java switch statement efficiency of default placement

Does the placing of default statement affect efficiency in Java? Is there difference between:
switch (a) {
case 0: return 0;
case 1: default: return -1;
case 2: return 2
...
case 99: return 99;
}
vs
switch (a) {
case 0: return 0;
case 1: return -1;
case 2: return 2;
...
case 99: return 99;
default: return -1;
}
Even in the most naive implementation this cannot produce a difference in performance, but with Java you are so far removed from the actual machine code this will turn into that you should definitely never even attempt to optimize this or any other similar piece of code. In fact, even if you wrote the dumbest cascade of else-ifs, you'd still stand a solid chance of JIT turning that into a superfast hash-lookup-based machine code.
No, it won't make a difference. Why? Because of this: When does the JVM know that it has to use the default body? After it checked all the other cases. So placing default: at a specific location in the which won't change performance.
I don't believe you'd see any significant performance improvements if you wrote a quick test program. At least none that would justify obscuring your code. I'd just leave the default case neatly last in the list to keep the code more readable. It's probably more important than a micro improvement in performance (if any at all)
No. The location of the switch statement makes no difference to the generated byte code other than the debugging annotations.

Is there a use of free floating block inside a method in Java?

I didn't know methods could have floating blocks like this:
class X {
public static void main( String [] args ) {
{ //<--- start
int i;
} //<-- ends
}
}
I was aware of floating blocks outside methods, but never tried them inside.
This could probably be used to define local scope or something.
Is there a use for floating blocks inside methods in Java?
Is there a use?
Yes - to limit the scope of local variables.
Is it a good idea?
Probably debatable (and likely will be).
The "pro" camp will say it never hurts to narrow the scope of variable. The "con" camp will say that, if you use it in a method and your method is long enough to warrant narrowing the scope of variables to specific sections, then it is probably an indication that you should make separate methods out of the different sections.
Personally, I use them, e.g.:
public void doGet(
final HttpServletRequest request,
final HttpServletResponse response)
throws IOException {
final PersistenceManager pm = PMF.get().getPersistenceManager();
final List<ToDoListGaejdo> toDoLists;
{
final Query query = pm.newQuery(ToDoListGaejdo.class);
query.setOrdering("listName asc");
toDoLists = (List<ToDoListGaejdo>) query.execute();
}
final List<ToDoItemGaejdo> entries;
{
final Query query = pm.newQuery(ToDoItemGaejdo.class);
query.setOrdering("listName asc, priority asc");
entries = (List<ToDoItemGaejdo>) query.execute();
}
final ServletOutputStream out = response.getOutputStream();
out.println("<head>");
out.println(" <title>Debug View</title>");
....
Yes, they are useful for two purposes.
First, you can limit the scope of variables using such blocks. This can help with the readability of code, because you have a smaller area to consider when determining how a variable is used. Additionally, this can minimize the required number of "slots" for the method frame, conserving stack space. I'd be very surprised to find a case where this optimization was necessary.
Second, when you attach a label to the block, a targeted break can be used inside the block to jump to the end of the block. This is not quite as evil as an unadulterated goto statement, but a sighting still triggers red flags and warning bells.
You've just given an example yourself. You can make the declaration of i 'more local' than method scope. When it goes out of scope, the rest of the method can't see it.
Syntactically, a block can appear anywhere a statement can appear. This is the fundamental principle of block-structured languages.
One further case where blocks are useful is in switch statements; e.g. the following is invalid
switch (i) {
case 1:
float j = ...;
break;
case 2:
float j = ...; // compilation error
break;
...
}
but it is OK with added blocks:
switch (i) {
case 1:
{
float j = ...;
break;
}
case 2:
{
float j = ...; // just fine
break;
}
...
}
This kind of use-case arises when you implement things like state machines and interpreters using (big) switch statements. You could argue that the local variables should all have different names, but:
using the same name for the same concept (though in a different scope) in gnarly code like this is good for large-scale readability, and
using the blocks makes it easier for the compilers to recognize that the variables declared within may share stack frame slots. This is potentially significant if the enclosing method is also recursive.
(OK, this is all either micro-optimization, or a consequence of micro-optimization. But let's assume we've determined that it is justified.)

Categories

Resources