72

The program that automatically grades my code is docking me "style-points" for an else-if that doesn't execute any code. It says that it may cause an error, but I don't think it could.

I'm not sure how to change it so that it still works but doesn't break the rule. Why is doing this bad form? I think any other way I write it will be harder for a reader to understand. How should it be written instead?

if (! seesWater(LEFT))
{
    turn(LEFT);
}
else if (! seesWater(AHEAD));
else if (! seesWater(RIGHT))
{
    turn(RIGHT);
}
else
{
    turn180();
}

The reason the else-if is there but does nothing is because of the priority in which I want the code to act:

if (! seesWater(AHEAD)), then I don't want the rest of the conditions to run at all because they don't matter.

Simson
  • 3,373
  • 2
  • 24
  • 38
Henry Kozurek
  • 739
  • 1
  • 4
  • 4
  • 5
    Assuming this code is in a method (and if it isn't, it should be), you should be terminating the method using `return` rather than keeping it blank. – qwerty Sep 23 '20 at 01:19
  • 27
    I don't agree that a return should be used here. We're not looking at a complete function or method here. Who's to say there isn't more code after this? – CryptoFool Sep 23 '20 at 01:27
  • 2
    Furthermore, sticking a return in one clause, but not all, makes a special case which then makes it more difficult to see the overall structure. – J.Backus Sep 23 '20 at 11:14
  • 34
    Personally I feel like the semicolon after `(! seesWater(AHEAD))` is a little hard to spot. Part of it is because `))` and `));` look similar, and part of it is because it's a little out of the ordinary to immediately follow an `if` clause with a semicolon. Maybe `{ }` might be better suited for this case. – Panzercrisis Sep 23 '20 at 15:17
  • 15
    Putting semicolon on the same line is often a typo; many style checkers will warn about that, and you need to use either an empty block `{}` or put the semicolon on the next line to silence them. – Barmar Sep 23 '20 at 15:21
  • 6
    @Panzercrisis: Not only is it a little hard to spot, it's also hard to understand what the author expects the code to be doing after you've spotted it, since IMHO most people just wouldn't do that. – jamesqf Sep 23 '20 at 16:51
  • 1
    I like the suggestions about putting a comment in the AHEAD block. Right now on first glance I might take it as a fall-through case on a switch statement because of how it lines up with the condition below it. – xdhmoore Sep 23 '20 at 21:56
  • Related: https://stackoverflow.com/questions/445700/ – user Sep 24 '20 at 01:38
  • The problem with `if (...);` is that many people have written things like `if (...); { ... }` and have been confused about why the code in the `{ ... }` always executes instead of only when the condition in the if-statement is true. – Bernhard Barker Sep 24 '20 at 07:23
  • 2
    Is anybody else bothered by the turn180 method? I mean we are mixing nomenclature in one case we use left and right, but in another we use degrees. We have a method that handles left and right turns based on a parameter, but we have to have a separate method to turn around? Why not turn(back)? – Glen Yates Sep 24 '20 at 14:51
  • 1
    Please unclutter your code and don't waste so many nearly empty lines; follow an accepted style like https://google.github.io/styleguide/javaguide.html – Martin Schröder Sep 24 '20 at 21:48
  • I don't know what your specific auto-style checker is looking for specifically, but I've worked on software where an empty if statement *must* have braces and it *must* have a comment stating that nothing should be done, to show that it wasn't an oversight on the developers part nor a copy/paste error. – Cort Ammon Sep 26 '20 at 18:08
  • Is there a reason why you can't just... remove the empty else if entirely? If it's not used, get rid of it, the code will still function the same way. #YAGNI. – s89_ Sep 29 '20 at 23:34
  • Its a recommendation to use always braces - see https://www.oracle.com/java/technologies/javase/codeconventions-statements.html#449 also using a `;` alone as a statement is hard to understand and looks like a typo. It works - well, but for other people it is hard to maintain/understand. – de-jcup Oct 15 '20 at 06:26

13 Answers13

144

Who says it's "bad style"?

The relevant question to ask is, is this clearer than alternatives? In your specific case, I'd say it is. The code clearly expresses a choice between 4 options, of which one is "do nothing".

The only change I'd make is to replace that rather insignificant semicolon by an empty pair of braces, possibly with a comment to make it clear it's not a mistake.

if (! seesWater(LEFT)) {
    turn(LEFT);
}
else if (! seesWater(AHEAD)) {
    // nothing required
}
else if (! seesWater(RIGHT)) {
    turn(RIGHT);
}
else {
    turn180();
}

This is not endorsing 'empty clauses' as a generally-acceptable style; merely that cases should be argued on their merits, not on the basis of some Rule That Must Be Obeyed. It is a matter of developing good taste, and the judgement of taste is for humans, not mindless automata.

J.Backus
  • 1,441
  • 1
  • 6
  • 6
  • 20
    Who says it's bad style? Checkstyle does. – Bohemian Sep 23 '20 at 01:51
  • 18
    If Checkstyle would rule the world, I couldn't write my name because it contains spaces. ;-) @J.Backus: I agree with you. Readable / easy understandable code is more useful, than than checkstyle errors. And I belive, that the compiler will optimize unnecessary statements away . – Mirko Sep 23 '20 at 07:01
  • 7
    It's a double negative, if there is no water ahead, do nothing. The entire premise is that there is water ahead, that should be checked first, and this logic is flawed because it could turn left forever. – rtaft Sep 23 '20 at 16:32
  • 4
    +1: it can be useful to add a check for the "do nothing" case. It can help get the program flow I want and also shows the next developer that I explicitly considered this case and didn't just forget it. Including a comment like "// do nothing" makes it clear it's not a forgotten implementation. – JounceCracklePop Sep 23 '20 at 17:40
  • 2
    This is the correct answer. There are four different cases, they should be written in a consistent and easy to read style. Just because one case doesn't need any action doesn't mean it should be handled differently. Or you can go back to the principles of structured programming: Imagine you want to log what decision was made. You need four logging statements, and as written, the code has four good places where to put the logging. – gnasher729 Sep 23 '20 at 22:08
  • 3
    I agree with Checkstyle. This is a code smell. This is not a choice between 4 options, it's a choice between 3 options. For every lone `if` you could say there are 2 options and add a useless empty `else` block. – xehpuk Sep 23 '20 at 23:04
  • @TripeHound this is not a programming exercise. this is an academic exercise. there are absolute rules of academic exercises. One of them is to figure out how to make the automatic code grader happy. But yeah, after refactoring this code to get a good grade I would not take it too seriously. – emory Sep 24 '20 at 13:24
  • 1
    @J... Or `// Do nothing. Keep going straight.`. "Do Nothing" is important because it makes it *extremely* clear what's going on. – Eliezer Berlin Sep 24 '20 at 14:25
  • 2
    *Who says it's "bad style"?*, by adding a comment in there, you unconsciously acknowledged that it *is* bad style. Always add a comment to make it clear that your empty block is intended and not a mistake. – Nolonar Sep 24 '20 at 21:24
  • Please don't tell me what I think. That I made a stylistic improvement does not mean that, before, it was "bad style". It's a continuum, not a binary attribute. – J.Backus Sep 24 '20 at 22:59
  • 1
    Before, we needed to go through 10 lines of code to understand the purpose of that empty bracket, which is now explained with a single comment consisting of only 2 short and simple words. Had the code been more complex, this could've meant a significant and unnecessary waste of time. I don't know about you, but that's obviously "bad style" to me, regardless of whether it's a binary or continuous attribute. – Nolonar Sep 25 '20 at 11:52
45

If this is the logic you want, there is nothing wrong with your code. In terms of code styling wise, I agree with the other users. Something like this would be clearer in my opinion:

if (! seesWater(LEFT))
{
    turn(LEFT);
}
else if (! seesWater(AHEAD))
{
    //Do nothing
}
else if (! seesWater(RIGHT))
{
    turn(RIGHT);
}
else
{
    turn180();
}

But by having priority of making a left turn over moving ahead (by doing nothing), the movement may end up in circles:

enter image description here

If you want the movement to "do nothing" but avoid moving into waters like this:

enter image description here

You may want to change your logic to something like this:

if (! seesWater(AHEAD))
{
    //Do nothing. Keep moving
}
else if (! seesWater(LEFT))
{
    turn(LEFT);
}
else if (! seesWater(RIGHT))
{
    turn(RIGHT);
}
else
{
    turn180();
}
user3437460
  • 17,253
  • 15
  • 58
  • 106
  • 25
    That last block of code would not produce the output that you have shown in your 2nd picture. After the initial left turn the code would continue straight (to the left edge of the picture), with no logic to make it turn right (to face up in the picture) – musefan Sep 23 '20 at 15:42
  • 3
    @musefan that’s impossible to say. Maybe the algorithm always takes the heading as UP when determining the next move. No heading is included in this algorithm, so it’s not reasonable to analyze it, IMO – Adam Jenkins Sep 23 '20 at 19:05
  • 5
    Change `turn180()` to `turn(AROUND)` and use `if(seesWater(AHEAD)) turn(!seesWater(LEFT)? LEFT: !seesWater(RIGHT)? RIGHT: AROUND);` – Holger Sep 24 '20 at 08:43
  • @Holger This is not a great use case for the ternary operator (needless to say two of them nested), the program reads more difficult when you try to use it for flow control. – Arthur Khazbs Jul 21 '22 at 15:48
  • 1
    @ArthurKhazbs these ternary operators are not more nested than the if statements of this answer. And since this is just a single operation with one contextual argument, this is a perfect use case for the ternary operator. But I take notice that after almost two years one guy with a different personal opinion showed up. – Holger Jul 21 '22 at 15:59
  • @Holger With all my respect to the ternary conditional operator, I disagree with you strongly. In this scenario, to turn left/right/around are completely different operations which likely lead to different consequences in further execution of the program. To read or debug such program would require a significantly closer look at the code. Talking about opinions, I suggest you contacted the person who is now replacing you at the last company you left as a programmer and who gets to maintain your legacy code — ask them how fun their job is and hope they don't know where you live. – Arthur Khazbs Jul 21 '22 at 16:12
  • 1
    @ArthurKhazbs the fact that there is a single method, `turn`, proves that these are *not* different operations. In most real life software, it boils down to something like, e.g. +90, -90, or +180 for an angle, or other coordinate transformations. Whatever it is, it has been well encapsulated in these constants you can choose as method argument, by the OP already. – Holger Jul 21 '22 at 16:20
  • @Hogler I see you would like to abstract from what goal the program tries to achieve (which is, apparently, to control some kind of robot as a homework for a programming basics course, not a code golf challenge). You would rather judge the program just by the syntax itself — so be it: the ternary operator's sole purpose is to choose between two options based on a condition, and in this program there clearly are more than two options, so you have to apply it multiple times in a nested, barely readable fashion, so the condition for each option is unnecessarily difficult to evaluate visually. – Arthur Khazbs Jul 21 '22 at 16:44
26

You can invert the ! seesWater(AHEAD) condition. Then you can move the code in its else clause to its if clause:

if (! seesWater(LEFT))
{
    turn(LEFT);
}
else if (seesWater(AHEAD)) 
{
    if (! seesWater(RIGHT))
    {
        turn(RIGHT);
    }
    else
    {
        turn180();
    }
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • 49
    I find the original code structure way clearer than this. – user2357112 Sep 23 '20 at 03:44
  • 7
    @user2357112supportsMonica Perhaps the reason why you find it unclear is because, as user3437460 pointed out in their answer, that the logic itself is a bit weird, but this question is not about the correctness of the logic, so I did not address that in my answer. The technique I described in my answer, "invert the `! seesWater(AHEAD)` condition, then move the code in its else clause to its if clause" can be applied to the last code snippet of user3437460's answer as well. If you do that, does that make it clearer than the original code? – Sweeper Sep 23 '20 at 04:03
  • 1
    @Sweeper I think this might be a maze solving algorithm... in which case it makes much more sense. https://en.wikipedia.org/wiki/Maze_solving_algorithm – user3067860 Sep 23 '20 at 19:07
  • 7
    This is the correct structure. `seesWater(AHEAD)` is the important condition. We don't care for `!seesWater(AHEAD)`. – xehpuk Sep 23 '20 at 23:08
  • 1
    This is what I ended up doing in order to get the points. But personally I think it's more clear the way I wrote it initially. I wish the automatic grading system didn't doc points for subjective reasons like this. – Henry Kozurek Sep 24 '20 at 02:07
13

I would argue that you can decide to not change the logic of your code at all. I think there's no reason to avoid an empty if or else if block if that leads to your code being the most readable and the logic most understandable. You can often rewrite your code to not include an empty code block and yet have it be just as understandable. But if not, I say this is fine.

One thing though...I don't like the style of using a ; to denote an empty code block. That's really easy to miss and is not normally seen, so it can confuse the reader. I'd suggest replacing the ; with an empty set of curly braces. You could also put a comment inside the empty curlies to make it clear you mean for that code block to be empty, ie // In this case, we don't want to do anything.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44
13

For this particular code sample, I don't believe the logic is accurate and does not flow intuitively. It looks like you want to turn if there is water ahead...but that left turn is in there that makes it confusing and potentially a bug. Ultimately I see the empty logic as a double negative, 'if there is no water ahead, do nothing', and double negatives are also frowned upon.

if (seesWater(AHEAD))
{
    if (! seesWater(LEFT))
    {
        turn(LEFT);
    }
    else if (! seesWater(RIGHT))
    {
        turn(RIGHT);
    }
    else
    {
        turn180();
    }
}

This makes the most sense since the additional logic is based on whether there is water ahead. It also makes it easier if you want to move the actions to another function if there is water ahead.

rtaft
  • 2,139
  • 1
  • 16
  • 32
  • 4
    @FlorianF I know, the op's code spins round in circles. – rtaft Sep 23 '20 at 19:50
  • 3
    The question was about style. – Florian F Sep 23 '20 at 19:57
  • 2
    @rtaft It could possibly be a maze solving algorithm... https://en.wikipedia.org/wiki/Maze_solving_algorithm – user3067860 Sep 23 '20 at 20:47
  • 6
    @FlorianF coding style isn’t an end in itself. Good coding style makes errors obvious and the correct logic look good. The logic of this answer can be simplified to [this statement](https://stackoverflow.com/questions/64019647/why-is-an-empty-else-if-statement-bad-style-and-how-should-i-rewrite-it#comment113248364_64020089) and suddenly, it’s pretty clear what’s going on and that it is the thing that should be going on. – Holger Sep 24 '20 at 08:47
  • What is expressed by the OP is to take the first available choice between left, ahead, right and back. You code says to try left, then if you cannot go straight, then try right and back. The symmetry between the 4 cases is not obvious. – Florian F Sep 24 '20 at 08:55
13

I find the negative logic in the if(!condition) statements hard to read and mind-twisting, even more so in combination with if-else-else-else. I would prefer a positive logic like seesLand() rather than !seesWater.

You could modify the turn() function, so that turn(AHEAD) does nothing and turn(BACK) does the 180 turn instead of needing a separate function for that.

Then you could rewrite this into.

List<Direction> directions = new ArrayList(LEFT, FORWARD, RIGHT, BACK);
for (Direction d: directions) {
    if(seesLand(d) {
        turn(d);
        return;
}
    

One upside is that this generalises even more. You could create different movement strategies simply by defining another array where you order the directions as you wish, and feed them into this function to check and turn in the chosen order.

user985366
  • 1,635
  • 3
  • 18
  • 39
  • 2
    It might be a good idea, but this is poorly executed. You cannot initialize an ArrayList like this. There's a missing paren, and your code potentially turns 4 times, instead of just once in OP's case. – Eric Duminil Sep 24 '20 at 19:31
  • 1
    I wasn't trying to write syntactically perfect code but to convey a few ideas. Could have made that clearer. Thanks for your comment. What do you mean by "turns 4 times" ? – user985366 Sep 25 '20 at 05:41
  • 1
    If you run OP's code, it calls `turn` at most once, and we don't know what happens afterwards. Your code can call `turn` 4 times, once for each direction, without any code in between. – Eric Duminil Sep 25 '20 at 07:56
  • 1
    Correct, the `return` in my example is misplaced and doesn't actually do anything. It was meant to break the execution but since it's in an inner function in the foreach, it doesn't. That was a mistake. It needs to be written differently, perhaps with the older style `for (Direction d: directions)` to work as I intended. I changed the code now to reflect what was the original intention. – user985366 Sep 25 '20 at 10:32
  • 1
    Sorry, but OP didn't use any `return`, so you shouldn't either. There might be more code present after the above snippet. One way to implement your idea would be with a stream, `filter`, `findFirst`, and `ifPresent`. – Eric Duminil Sep 25 '20 at 11:51
  • 1
    I disagree. Like one or more other answers mentioned above, if it wasn't already a separate function, it would make sense to make it a separate function. – user985366 Sep 25 '20 at 12:01
  • 1
    Fine, but you should mention it explicitly in your answer, then. And possibly fix the wrong ArrayList initialization. – Eric Duminil Sep 25 '20 at 12:04
10

What you do with something like this is you turn it into a speaking method and preferably split it up.

Option 1:

private turnTowardsGround(){
  if(! seesWater(AHEAD){
    return;
  }
  if (! seesWater(LEFT))
  {
     turn(LEFT);
     return;
  }
  if (! seesWater(RIGHT))
  {
    turn(RIGHT);
    return;
  }
  turn180();
}

Option 2:

private determineMoveDirection(){
  if(! seesWater(AHEAD){
    return AHEAD;
  }
  if (! seesWater(LEFT))
  {
     return LEFT
  }
  if (! seesWater(RIGHT))
  {
    return RIGHT
  }
  return BACK;
}

and then use Option 2 like:

Direction directionToMove = determineMoveDirection();
turn(directionToMove);

Why? Because the method names make the intend of the code clear. And the early return exits allow a quick read where we don't need to read the whole if-else code to make sure nothing else happens once one case applies. Plus in the option 2 case you have a clear separation of concerns (finding out where to move to vs actually turning), which is nice for modularity and in this case also allows one single place to log the final decision where you're going to turn, for example.

Also, preferably the checks would not be negated but there would be a "seesLand" or "seesAccessibleFieldType" method. It's psychologically easier/faster to parse non-negated statements and it makes it more clear what you are actually looking for (i.e. a land tile, a ice tile, ...).

Frank Hopkins
  • 639
  • 4
  • 12
8

Why would an empty-if clause be bad style? Because it is a non-obvious way of 'exiting' the if block. It skips over subsequent else-ifs and more importantly skips the final else, which in a multi-condition block ends up as the 'default' action.

To me there are two major problems here:

  1. The if-statement order matters, but it's unclear why this order is chosen
  2. The else-statement appears to have logic, which is skipped by do_nothing rather than being a catch-all

The first question I'd ask looking at code like this is why is the do_nothing option (! seesWater(AHEAD)) not the first thing we check? What makes ! seesWater(LEFT) the first thing we check? I'd hope to see a comment as to why this order was important as the ifs don't seem to be obviously exclusive (could seeWater in multiple directions).

The second question I'd ask is in what cases we expect to end up in the catch-all else statement. At the moment turn180() is the 'default', but it doesn't feel like very default-y behaviour to me to go back the way you've come if looking around fails for some reason.

David258
  • 697
  • 2
  • 6
  • 15
7

I'm coming from C, and living with MISRA, but to me an "if" with a semicolon is bad style regardless of whether there is an "else". Reason is that people don't normally put a semicolon there unless it's a typo. Consider this:

if (! seesWater(AHEAD));
{
  stayDry();
}

Here someone reading the code will think that you only stayDry() if you don't see water ahead -- you have to look hard to see that it will actually be called unconditionally. Much better, if you don't want to do anything in this case, is curly brackets and a comment:

if (! seesWater(AHEAD))
{
  /* don't need to do anything here */
}

In fact putting the curly brackets around the body of the "if", "else" etc. should always be done, and good on you for using the curly brackets even when the clause is a single turn() call! I sometimes find code like

if (! seesWater(AHEAD))
  stayDry();

then someone will come along later and add something else:

if (! seesWater(AHEAD))
  stayDry();
  doSomethingElse();

and of course the Something Else is done whether you see water or not, which was not the second coder's intention. You may think no-one would make a mistake that is so obvious but they do.

fpeelo
  • 372
  • 2
  • 10
5

The reason why a style checker reports something as poor style is that the people who implemented the style checker considered it poor style.

You'd have to ask them why, and their answer may, or may not, have merit.

Automated checkers of anything that require human intelligence should be treated with caution. Sometimes they produce useful output, and sometimes they don't. It's very unfortunate if such a checker is assessing the work of a human being, and extremely unfortunate if that assessment has consequences.

Kevin Boone
  • 4,092
  • 1
  • 11
  • 15
2

You don't do anything with the 2nd else if statement you should rewrite it like this

        else if(!seesWater(AHEAD)){return }

This is so the program has a conditional operator to return a boolean statement hope this helps -SG

swift gaming
  • 142
  • 4
  • 10
    I don't agree that a return should be used here. We're not looking at a complete function or method here. Who's to say there isn't more code after this? – CryptoFool Sep 23 '20 at 01:28
  • 3
    @Steve well, it's reasonable to turn it into a method – Frank Hopkins Sep 23 '20 at 22:50
  • 4
    But exiting the code flow and/or introducing a new function has nothing to do with the OPs question. You're basically saying, "rather than allow an empty if-else block, you should take the entire if/elseif/else sequence, put it in a function, and return from the function in the empty if-else block instead of leaving it empty" - I hope you'd agree that that's not the most straightforward answer to the question. – CryptoFool Sep 24 '20 at 02:58
0

For me it's a way to avoid forgetting a case later, especially if some of the options are different categories

// do nothing if ignoring
if(option=="red"){
    sound = "beep"
}else if(option=="blue"){
    sound = "beep"
}else if(option=="green"){
    sound = "long beep"
}else if(option=="ignore"){
    ; // do nothing
}

In that code case ignore is hard to forget however if you didn't have it, the outcome would be the same.

// do nothing if ignoring
if(option=="red"){
    sound = "beep"
}else if(option=="blue"){
    sound = "beep"
}else if(option=="green"){
    sound = "long beep"
}

then later if you decide that you want a the same beep for all colours so to simplify the code you could have

// do nothing if ignoring
sound = "beep"

which would work for the colors, but would also set sound to beep if ignoring, which we weren't doing previously. "But the comment", I hear you cry, well in longer code you might miss a comment and forget the ignore case because you might have been only thinking about the category of colours [of course automated testing can help you out]

So if I start from my first version it's harder to miss it, and I already have an if statement for the ignore case so I can rearrange

if(option=="ignore"){
    ; // do nothing
}else{
    sound = "beep"
}

Or

if(option!="ignore"){ sound = "beep" }
Robin
  • 104
  • 7
-1

Well we don't really know the context of the entire question. We don't know if this is the only code in a method. I'm not sure why you test for LEFT first. I would have thought you test for AHEAD first since I would think you continue in the same direction as the default if it is possible. If you keep going LEFT you will be walking in a circle. So given the lack of requirements it is hard to give a complete answer.

However, I would tend to agree that I don't like the empty if statement. If you want to keep walking AHEAD you need to indicate that somehow.

Other comments:

  1. I would rather have a positive method name like noWater(...). Then the if statement becomes a positive test instead of a negative test.

  2. Why do you have multiple forms of the method that you invoke: turn(LEFT), turn(RIGHT), and turn180()? Why is turn180() different? I would create a method that would accept a parameter for any direction.

Using the above suggestions you would have something like:

if (noWater(FORWARD))
    turn(0);
else if (noWater(LEFT))
    turn(270);
else if (noWater(RIGHT))
    turn(90);
else
    turn(180);

With a structure like this you don't have an empty if statement and the movement becomes parameterized (and explicit) so you can have different values as required.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • 2
    I find this considerably less understandable if this version has the exact same effect, for one key reason; I know that an empty block does nothing. How am I supposed to know what a `go(0)` call does? Research, I guess. Also, turning `turn` into `go` was another step in the wrong direction for readability. Here, I can only guess that `go(0)` means "turn 0 degrees" which means "don't do anything". `go(0)` might mean "do a life boat drill, hail for ships in the area, and then turn X (0) degrees". I have no way to know. The original left me with no doubt what would happen...nothing. – CryptoFool Sep 23 '20 at 03:29
  • Agreed the method name should have been simplified to `turn(0)`. The parameter would then be more obvious. But not really worth the time debating since the OP hasn't even replied to any of the suggestions. – camickr Sep 23 '20 at 23:50