I have a history in programming, but not much in software development. I'm currently writing a piece of software for the company I work at, and I've come to challenge myself on the readability of my code.
I want to know whether this is a "valid" alternative to embedded if statements, or if there is anything better I could use.
Let's say I have the following method:
public void someMethod()
{
if (some condition)
{
if (some condition 2)
{
if (some condition 3)
{
// ...etc all the way until:
doSomething();
}
else
{
System.err.println("Specific Condition 3 Error");
}
}
else
{
System.err.println("Specific Condition 2 Error");
}
}
else
{
System.err.println("Specific Condition 1 Error");
}
}
Now the first thing I should point out is that in this instance, combining the conditions (with &&) isn't possible, since each one has a unique error that I want to report, and if I combined them I wouldn't be able to do that (or would I?). The second thing I should point out before anyone screams "SWITCH STATEMENT!" at me is that not all of these conditions can be handled by a switch statement; some are Object specific method calls, some are integer comparisons, etc.
That said, is the following a valid way of making the above code more readable, or is there a better way of doing it?
public void someMethod()
{
if (!some condition)
{
System.err.println("Specific Condition 1 Error");
return;
}
if (!some condition 2)
{
System.err.println("Specific Condition 2 Error");
return;
}
if (!some condition 3)
{
System.err.println("Specific Condition 3 Error");
return;
}
doSomething();
}
So basically, instead of checking for conditions and reporting errors in else blocks, we check for the inverse of the condition and return if it is true. The result should be the same, but is there a better way of handling this?
If I was being particularly pedantic I would use something like this.
boolean c1, c2, c3;
public void someMethod() {
boolean ok = true;
String err = "";
if (ok && !(ok &= c1)) {
err = "Specific Condition 1 Error";
}
if (ok && !(ok &= c2)) {
err = "Specific Condition 2 Error";
}
if (ok && !(ok &= c3)) {
err = "Specific Condition 3 Error";
}
if ( ok ) {
doSomething();
} else {
System.out.print(err);
}
}
You are now single-exit AND flat.
Added
If &= is difficult for you, use something like:
if (ok && !c3) {
err = "Specific Condition 3 Error";
ok = false;
}
I would write it as
if (failing condition) {
System.err.println("Specific Condition 1 Error");
} else {
somethingExpensiveCondition2and3Dependon();
if (failing condition 2)
System.err.println("Specific Condition 2 Error");
else if (failing condition 3)
System.err.println("Specific Condition 3 Error");
else
doSomething();
}
yes, your code in both cases smells of conditional complexity (code smells)
Java is an OOP language, so your code should be factored to in the spirit of OOD, something like this:
for (Condition cond : conditions) {
if (cond.happens(params))
cond.getHandler().handle(params);
}
conditions list should be injected to this class, this way when a new condition is added or removed the class doesn't change. (open close principle)
Your second approach is fairly good. If you want something a little more baroque, you can move your conditions into Callable objects. Each object can also be provided with a way of handling errors. This lets you write an arbitrarily long series of tests without sacrificing functionality.
class Test {
private final Callable<Boolean> test;
private final Runnable errorHandler;
public Test(Callable<Boolean> test, Runnable handler) {
this.test = test;
errorHandler = handler;
}
public boolean runTest() {
if (test.call()) {
return true;
}
errorHandler.run();
return false;
}
}
You could then organize your code as follows:
ArrayList<Test> tests;
public void someMethod() {
for (Test test : tests) {
if (!test.runTest()) {
return;
}
}
doSomething();
}
EDIT
Here's a more general version of the above. It should handle almost any case of this type.
public class Condition {
private final Callable<Boolean> test;
private final Runnable passHandler;
private final Runnable failHandler;
public Condition(Callable<Boolean> test,
Runnable passHandler, Runnable failHandler)
{
this.test = test;
this.passHandler = passHandler;
this.failHandler = failHandler;
}
public boolean check() {
if (test.call()) {
if (passHandler != null) {
passHandler.run();
}
return true;
}
if (errorHandler != null) {
errorHandler.run();
}
return false;
}
}
public class ConditionalAction {
private final ArrayList<Condition> conditions;
private final Runnable action;
public ConditionalAction(ArrayList<Condition> conditions,
Runnable action)
{
this.conditions = conditions;
this.action = action;
}
public boolean attemptAction() {
for (Condition condition : conditions) {
if (!condition.check()) {
return false;
}
}
action.run();
return true;
}
}
One might be tempted to add some sort of generic data that could be passed around to share info or collect results. Rather than doing that, I'd recommend implementing such data sharing within the objects that implement the conditions and action, and leave this structure as is.
For this case, that's about as clean as you are going to get it, since you have both custom criteria and custom responses to each condition.
What you are in essence doing is validating some conditions before calling the doSomething() method. I would extract the validation into a separate method.
public void someMethod() {
if (isValid()) {
doSomething();
}
}
private boolean isValid() {
if (!condition1) {
System.err.println("Specific Condition 1 Error");
return false;
}
if (!condition2) {
System.err.println("Specific Condition 2 Error");
return false;
}
if (!condition3) {
System.err.println("Specific Condition 3 Error");
return false;
}
return true;
}
Nope, that's about what you get in Java. If you have too many of these, it may indicate that you should refactor a bit, and possibly even rethink your algorithm -- it may be worthwhile trying to simplify it a bit, because otherwise you're going to come back to the code in a few months and wonder why the heck a + b + c + d = e but a + b' + c + d = zebra
The second option you have is the more readable one. While multiple returns are usually not recommended putting all of them at the beginning of the code is clear (it isn't as if they are scattered all over the method). Nested ifs on the other hand, are hard to follow and understand.
Related
In case of assignment the situation is simple,
result = testCondition ? value1 : value2;
But what if I want to use it instead of an if statement?
for instance in a logging situation:
logger.shouldDebbug ? logger.log("logging") : (what to do if not?);
In the case I don't what to do anything in the case of false, can I still use this Operator?
Yes you can if you wrap them in a returning function, but no you shouldn't.
In your example of the logger, let your logger output to void, discard the input when debugging isn't enabled.
You do not want to riddle your code with all these logging checks.
Perform a check as least and as central as possible.
Either have a check in the logger.log function if debugging is enabled, or replace the logger with a dummy mock that does nothing except accept input and immediately discard it.
If you use standard logging frameworks like log4j you can set debugging levels, where you show only info or more serious, only warnings or more serious, only errors or more serious.
The same goes for other "quick" checks. If you find yourself using a certain pattern a lot, write a utility class for it with a static method if need be, so you have one place, where you have to change stuff, instead of 200 code points that you have to update when going to production.
You could use it if you insist, by defining a meaningless variable and take advantage of the functions' side-effects, but that's not a very good coding habit. It's purely a work-around.
For example:
public static boolean test() {
return 1>0;
}
public static int success() {
System.out.println("true");
return 0; // has no meaning whatsoever
}
public static int fail() {
System.out.println("false");
return 0; // has no meaning whatsoever
}
public static void main(String[] args) {
int meaningless = test() ? success() : fail();
}
Everything has been explained in comments, so I will put here only some idea:
public class Ternary {
private final boolean condition;
private Ternary(boolean condition) { this.condition = condition; }
public static Ternary of(boolean condition) { return new Ternary(condition); }
public Ternary onTrue(Runnable r) { if (condition) { r.run(); } return this; }
public Ternary onFalse(Runnable r) { if (!condition) { r.run(); } return this; }
}
Example of usage:
Ternary.of(o != null).onTrue(() -> doSomething()).onFalse(() -> doSomethingElse());
But simplier would be to write:
if (o != null) { doSomething(); } else { doSomethingElse(); }
Given a simple if statement in the form
public static String test(boolean flag) {
if (!flag) {
return "no";
} else {
System.out.println("flag enabled");
}
return "yes";
}
or
public static String test(final boolean flag)
{
if (flag)
{
System.out.println("flag enabled");
return "yes";
}
else
{
return "no";
}
}
Eclipse gives me the warning, underlining the whole else block
Statement unnecessarily nested within else clause. The corresponding then clause does not complete normally
However, this...
public static String test(final boolean flag)
{
if (flag)
{
System.out.println("flag enabled");
return "yes";
}
return "no";
}
does not give the warning.
this question seems related, but I'm not using return in the else of the first example, and else is not finally.
I understand that this is simply a preference, and that it can be turned off. However, I don't like ignoring things simply because I don't understand what is the problem. And I can't seem to find any documentation on why Eclipse added this as a configurable warning.
So in all, What does The corresponding then clause does not complete normally mean? What is the problem that this warning is trying to protect me from?
You can remove the else clause and that will remove the warning.
To answer your other question, "The corresponding then clause does not complete normally" is referring to the fact that the "then" clause has a return statement. Completing "normally" means control flow continues to the statements after the "else" block, but this doesn't happen since you returned.
There is no logical, functional, or runtime difference between:
// Gets warning
public static String test(final boolean flag) {
if (flag) {
System.out.println("flag enabled");
return "yes";
} else {
return "no";
}
}
and
// Doesn't get warning
public static String test(final boolean flag) {
if (flag) {
System.out.println("flag enabled");
return "yes";
}
return "no";
}
So it boils down to a style preference. Why might Eclipse think this is worth a warning? Think of it as similar to "unreachable code" -- an aspect of the code is unnecessarily (and perhaps unusually) verbose, and this usually merits another look to ensure that no logic errors are hiding. If I wrote the first code snippet and got the warning, I'd be thankful for Eclipse and happily improve the code, arriving at the second snippet. But again, that's just because I prefer the second snippet.
Here's a scenario where this might come into play and affect functionality (unintentionally). Suppose the code originally looks like this:
// Returns true if the dog was fed
boolean feedDog(Dog dog) {
if (!dog.isHungry()) {
return false;
}
Food food;
if (dog.isBadToday()) {
food = getDefaultFood();
} else {
food = getTreat();
}
dog.feed(food);
return true;
}
Sometime later, the team decides that feedDog should only return true if the dog was fed a treat. What if the code is modified to:
// Returns true if the dog was fed *a treat*
boolean feedDog(Dog dog) {
if (!dog.isHungry()) {
return false;
}
Food food;
if (dog.isBadToday()) {
food = getDefaultFood();
return false; // Dog wasn't fed a treat
} else {
food = getTreat();
}
dog.feed(food);
return true;
}
Now there's a bug because the programmer went with the shortest change that "looked right at first glance" -- the poor dog doesn't get fed if it misbehaved. Eclipse's warning is saving the dog here, telling the programmer to pay attention to the logic surrounding the if statement, which doesn't seem right anymore (according to Eclipse). Hopefully the programmer will heed the warning and avoid returning from the if block:
// Returns true if the dog was fed *a treat*
boolean feedDog(Dog dog) {
if (!dog.isHungry()) {
return false;
}
Food food;
boolean fedTreat = false;
if (dog.isBadToday()) {
food = getDefaultFood();
} else {
food = getTreat();
fedTreat = true;
}
dog.feed(food);
return fedTreat;
}
So say I have an if statement like if (bool1 && bool2) {}.
I want to display different error messages depending on which of these booleans failed. Normally I'd do something like:
if (bool1) {
if (bool2) {
// do something
} else {
// throw error for bool2 failing
}
} else {
// throw error for bool1 failing
}
Now I only know for sure that bool1 failed, but bool2 might be bad as well. Therefor, I'd use something like:
if (bool1 && bool2) {
// do something
} else {
if (!bool1) {
// throw error for bool1 failing
}
if (!bool2) {
// throw error for bool2 failing
}
...
}
However, this can get very long if there are a lot of variables to check, and especially if you want to display different errors if multiple variables fail.
// continuing on the dots of the above code block
if (!bool1 && !bool2) {
// throw error for both failing
}
Is there a more effective method for doing this?
Edit: Because all given answers are viable, and which is better is subjective, I'm not going to accept any as 'the' answer. Be sure to pick the method/answer that works best for your own needs.
Thank you all for your suggestions.
The best way I can think of is to not nest your if statements. Instead, I would check every error condition independently and accumulate the error messages in a StringBuilder:
StringBuilder errorMsg = new StringBuilder();
if (!bool1) {
errorMsg.append("bool1 condition failed\n");
}
if (!bool2) {
errorMsg.append("bool2 condition failed\n");
}
if (!bool3) {
errorMsg.append("bool3 condition failed\n");
}
// etc
if (errorMsg.length() > 0) {
throw new SomeException(errorMsg.toString());
}
// safely execute your code here
Check the useful StringBuilder class docs for further reference.
It isn't clear to me what is your scenario, but maybe I would approach your problem in this way (similar to DAB's answer):
List<String> errors = new ArrayList<String>();
if(!bool1){
errors.add("Error message 1");
}
if (!bool2) {
errors.add("Error message 2");
}
// other errors
if(!errors.isEmpty()){
throw new Exception(buildErrorsMessage(errors));
}
No, there is no way to do that, as far as I know. The way to do this more effectively is to restructure your code, so you do not need to nest if-clauses too deeply.
Wha dont you reverse your if ?
if(!bool1)
{
if(!bool2)
{
error both
}
error bool1
}
else if(!bool2)
{
error 2
}
else
do somthing
This is how I would normally do it...
String error=null;
if( a )
{
error="Error a";
}
else if( b )
{
error="Error b";
}
else if( c )
{
error="Error c";
}
if( error!=null )
{
throw new Exception(error);
}
I would do it like this
String error=null;
if(!bool)
error="Something";
else if(!bool2)
error="Other message";
else if(!bool3)
error="failed??";
// ...
if(error!=null) {
throw new Exception(error);
}
If you need to throw different types of exceptions, you can have error be of type Exception, instead of string, and then create the exception in the if(!bool1)
If you have a lot of booleans that should all be in one state you could throw them in an array and see if the array contains the opposite state. You can use a map if you need to keep track of the variables names for the error message. When generating the error message you can loop over the map and check each value.
package com.idfbins.questions;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class maptest {
public static void main(String[] args) {
Map<String, Boolean> maparini = new HashMap<String, Boolean>();
maparini.put("variable1", true);
maparini.put("variable2", true);
maparini.put("variable3", true);
maparini.put("variable4", true);
maparini.put("variable5", true);
System.out.println("All are true? " + allAreTrue(maparini));
System.out.println("All are false? " + allAreFalse(maparini));
System.out.println("Errors for all true: ");
errorsForTrue(maparini);
System.out.println("Errors for all false: ");
errorsForFalse(maparini);
}
public static boolean allAreTrue(Map<String, Boolean> maparini){
return !maparini.containsValue(false);
}
public static boolean allAreFalse(Map<String, Boolean> maparini){
return !maparini.containsValue(true);
}
public static void errorsForFalse(Map<String, Boolean> maparini){
for(Entry<String, Boolean> mapariniEntry : maparini.entrySet()){
if(mapariniEntry.getValue()){
System.out.println(mapariniEntry.getKey() + " was true!");
//You can construct a string to throw in an exception here
}
}
}
public static void errorsForTrue(Map<String, Boolean> maparini){
for(Entry<String, Boolean> mapariniEntry : maparini.entrySet()){
if(!mapariniEntry.getValue()){
System.out.println(mapariniEntry.getKey() + " was false!");
//You can construct a string to throw in an exception here
}
}
}
}
You could do something like this:
public static <T extends Throwable> void throwIf(boolean condition, Supplier<T> e) throws T {
if(condition) throw e.get();
}
throwIf(!bool1 && !bool2, () -> new Exception("big oops"));
throwIf(!bool1, MyException::new);
throwIf(!bool2, () -> new Exception("oops"));
throwIf(!bool3, Error::new);
// do something
if (!bool1 && !bool2) {
// throw error for bool1 && bool2 failing
} else if (!bool1) {
// throw error for bool1 failing
} else {
// throw error for bool2 failing
}
I have a function when has an if-else statement. It essentially looks like this:
if(boolean == true)
{
// do something
boolean = false;
}
else if(boolean == false)
{
// do the other thing
boolean = true;
}
Now, my understanding is that the if statement will exit and return control to the function and then continue according to the changed boolean value. But I'm clearly missing something because my code is not exiting the original 'if'/'else if' statement (whichever the original case). Can anyone tell me what I've missed?
Well as requested, additional data about the code is that it is a part of my android project and each condition in the if-else block has a nested function and the boolean(global) value is being set/unset withing these functions. So the code now looks like this:
dummyFunction(){
boolean = checkIfTrueOrFalse();
if (boolean) {
onClick( public void onClick(){
// do something
boolean = false;}
} else if(boolean == false){
onClick( public void onClick(){
// do something
boolean = true;}
}
}
Any ideas?
if(boolean == true)
{
// do something
boolean = false;
}
if (boolean == false)
{
// do the other thing
boolean = true;
}
When you do this, then the program will flow to the second condition. In an if/else if statement, if the if statement has been satisfied, then the program will ignore the else if block.
Your current code simply flows through the first if block and then skips the else if statement to end the block.
void someMethod()
{
boolean aBoolean = true;
if(aBoolean == true)
{
// do something
aBoolean = false;
}
else if(aBoolean == false)
{
// do the other thing
aBoolean = true;
}
}
When someMethod will execute, since aBoolean is assigned with true, control will come to if block cause the condition becomes true. if it was false, then the control will come to else part.
We have many good answers/comments already but just wanted to add something here -
1.
if (condition) {
} else {
}
is a single code construct. The condition will be evaluated at the beginning at run time and java will decide which block to execute i.e. the if block or the else block. Only 1 of the 2 can be executed.
Java allows us to nest if/else. That means we can have something like below -
if(condition1){
} else if (condition2) {
} else if (condition3) {
} else {
}
It is effectively same as below -
if (condition1) {
} else {
if (condition2) {
} else {
if (condition 3) {
} else {
}
}
}
Here, it should be noted that only the block which satisfies the condition will be executed. If none of the conditions is met, then the inner most else will be executed (i.e. the else block of condition3 )
Finally, I feel that your confusion is between the below blocks
boolean aBoolean = true;
if(aBoolean == true)
{
// do something
aBoolean = false;
} else if(aBoolean == false)
{
// do the other thing
aBoolean = true;
}
VS
boolean aBoolean = true;
if(aBoolean == true)
{
// do something
aBoolean = false;
}
if(aBoolean == false)
{
// do the other thing
aBoolean = true;
}
In the latter of the 2 examples, there are 2 independent if blocks and both will get executed (off course, this is not logically correct but it is a legal java code.)
Could you provide more info regarding your code not exiting either of the 2 blocks? Doing System.out.println() of variables within your blocks might be able to help you determine why your code is not exiting.
You could use an if/else pair instead of an if/else-if as the parameter that your code depends on is would be either true/false. If the if-block is not satisfied, automatically the else-block would be traversed.
Your code is actually a shortcut for
if (boolean) {
// do something
boolean = false;
} else {
if (!boolean) {
// do the other thing
boolean = true;
}
}
Written this way, it maybe becomes clearer that the inner if nested in the else case will not be processed if the first if condition was already met.
Well I've solved it (taking inputs from here of course). I just added a call to the function within the nested functions and it worked. Now the code looks like this:
public static void dummyFunction(){
boolean = checkIfTrueOrFalse();
if (boolean) {
onClick( public void onClick(){
// do something
dummyFunction();
boolean = false;}
} else if(boolean == false){
onClick( public void onClick(){
// do something
dummyFunction();
boolean = true;}
}
}
I'd like to call a method that either returns false, or an integer. At the moment my code is:
int winningID = -1;
if((ID = isThereAWinner()) != -1) {
// use the winner's ID
} else {
// there's no winner, do something else
}
private int isThereAWinner() {
// if a winner is found
return winnersID;
// else
return -1;
}
I don't like the if((ID = isThereAWinner()) != -1) bit as it doesn't read very well, but unlike C you can't represent booleans as integers in Java. Is there a better way to do this?
I would use something similar to Mat's answer:
class Result {
public static Result withWinner(int winner) {
return new Result(winner);
}
public static Result withoutWinner() {
return new Result(NO_WINNER);
}
private static final int NO_WINNER = -1;
private int winnerId;
private Result(int id) {
winnerId = id;
}
private int getWinnerId() {
return winnerId;
}
private boolean hasWinner() {
return winnerId != NO_WINNER;
}
}
This class hides the implementation details of how you actually represent if there were no winner at all.
Then in your winner finding method:
private Result isThereAWinner() {
// if a winner is found
return Result.withWinner(winnersID);
// else
return Result.withoutWinner();
}
And in your calling method:
Result result = isThereAWinner();
if(result.hasWinner()) {
int id = result.getWinnerId();
} else {
// do something else
}
It may seem a little bit too complex, but this approach is more flexible if there would be other result options in the future.
What about something like:
private int getWinnerId() {
// return winner id or -1
}
private boolean isValidId(int id) {
return id != -1; // or whatever
}
int winnerId = getWinnerId();
if (isValidId(winnerId)) {
...
} else {
...
}
This is all quite subjective of course, but you usually expect an isFoo method to provide only a yes/no "answer".
The problem is you are trying to return two values at once. The approach you have taken is the simplest for this. If you want a more OO or design pattern approach I would use a listener pattern.
interface WinnerListener {
void onWinner(Int winnerId);
void noWinner();
}
checkWinner(new WinnerListener() {
// handle either action
});
private void checkWinner(WinnerListener wl) {
// if a winner is found
wl.onWinner(winnersID);
// else
wl.noWinner();
}
This approach works well with complex events like multiple arguments and multiple varied events. e.g. You could have multiple winners, or other types of events.
I'm afraid not. To avoid errors caused by mistaking if(a == b) for if(a = b), Java removes the conversion between boolean type and number types. Maybe you can try exceptions instead, but I think exception is somewhat more troublesome. (My English is not quite good. I wonder if I've made it clear...)
Perhaps you may wish to consider exceptions to help you with your understanding of asthetics of coding.
Use Integer instead of int and return null instead of -1. Look from this point: "I am returning not integer, but some object that represents winner identity. No winner - no instance"
Joe another suggestion, this is constructed based on #Mat and #buc mentioned little while ago, again this is all subjective of course I'm not sure what the rest of your class/logic is. You could introduce an enum with different ResultStatuses if it makes sense within the context of your code/exmaple.
As Matt mentioned you would expect isValid method to return a boolean yes/no (some may also complain of readability)
public enum ResultStatus {
WINNER, OTHER, UNLUCKY
}
This could be an overkill as well and depends on the rest of your logic (and if logic is expanding) but I thought I'll suggest nonetheless my two cents! So therefore in your public class (similar to #bloc suggested) you could have a method such as below that will return the status of the result checked.
public ResultStatus getResultStatus() {
if (isWinner()) {
return ResultStatus.WINNER;
} else {
return isOtherCheck() ? ResultStatus.OTHER : ResultStatus.UNLUCKY;
}
}