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.

14
  • 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
    Commented Sep 23, 2020 at 1: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
    Commented Sep 23, 2020 at 1:27
  • 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. Commented Sep 23, 2020 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
    Commented Sep 23, 2020 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
    Commented Sep 23, 2020 at 16:51

13 Answers 13

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.

11
  • 20
    Who says it's bad style? Checkstyle does.
    – Bohemian
    Commented Sep 23, 2020 at 1: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
    Commented Sep 23, 2020 at 7: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
    Commented Sep 23, 2020 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. Commented Sep 23, 2020 at 17:40
  • 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
    Commented Sep 23, 2020 at 23:04
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();
}
8
  • 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
    Commented Sep 23, 2020 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 Commented Sep 23, 2020 at 19:05
  • 5
    Change turn180() to turn(AROUND) and use if(seesWater(AHEAD)) turn(!seesWater(LEFT)? LEFT: !seesWater(RIGHT)? RIGHT: AROUND);
    – Holger
    Commented Sep 24, 2020 at 8:43
  • 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
    Commented Jul 21, 2022 at 15:59
  • 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
    Commented Jul 21, 2022 at 16:20
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();
    }
}
5
  • 51
    I find the original code structure way clearer than this. Commented Sep 23, 2020 at 3: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
    Commented Sep 23, 2020 at 4:03
  • 1
    @Sweeper I think this might be a maze solving algorithm... in which case it makes much more sense. en.wikipedia.org/wiki/Maze_solving_algorithm Commented Sep 23, 2020 at 19:07
  • 7
    This is the correct structure. seesWater(AHEAD) is the important condition. We don't care for !seesWater(AHEAD).
    – xehpuk
    Commented Sep 23, 2020 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. Commented Sep 24, 2020 at 2: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.

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.

5
  • 4
    @FlorianF I know, the op's code spins round in circles.
    – rtaft
    Commented Sep 23, 2020 at 19:50
  • 3
    The question was about style.
    – Florian F
    Commented Sep 23, 2020 at 19:57
  • 2
    @rtaft It could possibly be a maze solving algorithm... en.wikipedia.org/wiki/Maze_solving_algorithm Commented Sep 23, 2020 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 and suddenly, it’s pretty clear what’s going on and that it is the thing that should be going on.
    – Holger
    Commented Sep 24, 2020 at 8: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
    Commented Sep 24, 2020 at 8: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.

7
  • 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. Commented Sep 24, 2020 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
    Commented Sep 25, 2020 at 5: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. Commented Sep 25, 2020 at 7: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
    Commented Sep 25, 2020 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. Commented Sep 25, 2020 at 11:51
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, ...).

8

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.

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.

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.

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

3
  • 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
    Commented Sep 23, 2020 at 1:28
  • 3
    @Steve well, it's reasonable to turn it into a method Commented Sep 23, 2020 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
    Commented Sep 24, 2020 at 2: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" }
-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.

2
  • 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
    Commented Sep 23, 2020 at 3: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
    Commented Sep 23, 2020 at 23:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.