Always write clean, readable code that you or any developer can maintain. In this post, we will learn 10 best practices for if statements and will show you some best practices to use if statements in Java.
1. Use Clear and Meaningful Conditions
2. Indentation and Formatting
3. Always use Braces
4. Avoid using nested if Statements
5. Consistent Use of Parentheses
6. Always use null Checks
7. Use Short-Circuiting
8. Avoid Complex Conditions
9. Use Constants or Enumerations
10. Use Switch-Case for Multiple Options
Video in Hindi
Video In English
1. Use Clear and Meaningful Conditions
It’s always good if we write a clear and meaningful condition in the if statement. Whenever we have a complex expression then break it down into smaller, well-named boolean variables. We will see it in the below example.
Here we are directly checking the condition for a leap year though the condition is not simple. That’s why code is less readable and hard to maintain. The same condition might be repeated at multiple places if we want to check the leap year at multiple points in the code.
import java.util.Scanner; public class LeapYearCheck { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Enter a year: "); int year = scanner.nextInt(); if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) { System.out.println(year + " is a leap year."); } else { System.out.println(year + " is not a leap year."); } } }
Let’s create a helper method “isLeapYear” to check the leap year logic and break down the condition in the separate boolean variable. Now the method “isLeapYear” will tell use the result.
- The code is easier, clean, and readable due to meaningful methods and variable names.
- The logic to check leap year is placed in one place and it’s easier to maintain and modify it
- It leads to the reusability of code, as it can be used in different parts of the code without repeating the logic.
import java.util.Scanner; public class LeapYearCheck { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Enter a year: "); int year = scanner.nextInt(); if (isLeapYear(year)) { System.out.println(year + " is a leap year."); } else { System.out.println(year + " is not a leap year."); } } // Helper method to check if a given year is a leap year public static boolean isLeapYear(int year) { // Check if the year is divisible by 4 boolean divisibleBy4 = year % 4 == 0; // Check if the year is divisible by 100 boolean divisibleBy100 = year % 100 == 0; // Check if the year is divisible by 400 boolean divisibleBy400 = year % 400 == 0; // Determine if it's a leap year based on the conditions return divisibleBy4 && (!divisibleBy100 || divisibleBy400); } }
2. Indentation and Formatting
To enhance readability and make easy-to-use nesting statements, we should keep consistent formatting. If the code lacks proper indentation and formatting, code looks cluttered and harder to read. Let’s see an example and see what are the best practices for if statements
import java.util.Scanner; public class EvenOddChecker { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter a number: "); int number = input.nextInt(); // Check if the number is even or odd if (number % 2 == 0) { System.out.println("The number is even."); } else { System.out.println("The number is odd."); } } }
Just have a look at line 10 where indentation is not proper. If any developer will see this then he will get confused about where the if statement starts.
import java.util.Scanner; public class EvenOddChecker { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter a number: "); int number = input.nextInt(); // Check if the number is even or odd if (number % 2 == 0) { System.out.println("The number is even."); } else { System.out.println("The number is odd."); } } }
3. Always use Braces
Always use curly braces, even for single-line if statements. Everyone knows that braces are not mandatory for a single line, but it is always a good practice to use braces to prevent potential bugs and improve code clarity. Let’s see how it’s a good practice.
In this example, we haven’t used the braces for the if statement. Just notice line 9 “System.out.println”, it’s not indented properly. And that might create a misconception that it is part of the if condition. However, in reality, the line will be executed unconditionally, regardless of the if condition’s outcome.
public class EmailNotificationWithoutBraces { public static void main(String[] args) { String username = "john_doe"; boolean emailNotificationsEnabled = true; boolean inboxEmpty = false; if (emailNotificationsEnabled && !inboxEmpty) System.out.println("Sending email notification to " + username); System.out.println("You have a new message in your inbox!"); } }
Let’s use the braces and see the impact. Here we are using braces to encapsulate the multiple lines of code inside the if statement. Now code looks much better and is completely readable.
public class EmailNotificationWithBraces { public static void main(String[] args) { String username = "john_doe"; boolean emailNotificationsEnabled = true; boolean inboxEmpty = false; if (emailNotificationsEnabled && !inboxEmpty) { System.out.println("Sending email notification to " + username); } System.out.println("You have a new message in your inbox!"); } }
4. Avoid using nested if Statements
Avoid using the nested if statement to make code more readable and clearer. Whenever you find a nesting multiple if statements, consider refactoring the code to make it more readable. You can achieve it by the use of logical operators. If you haven’t read them yet, you can read them here and the see the best practices for if statements
We want to create a program to check the eligibility of a person for a certain job based on their age and qualifications. Let’s see how we can achieve it by use of a nested If statement.
public class JobEligibilityWithNestedIf { public static void main(String[] args) { int age = 30; boolean hasDegree = true; boolean hasExperience = false; if (age >= 25) { if (hasDegree) { if (hasExperience) { System.out.println("Congratulations! You are eligible for the job."); } else { System.out.println("Sorry, you are not eligible due to lack of experience."); } } else { System.out.println("Sorry, you are not eligible due to lack of a degree."); } } else { System.out.println("Sorry, you are not eligible due to age requirement."); } } }
We used nested if statements to determine eligibility based on different conditions like age, degree, and experience. But this approach leads to deep nesting and makes the code harder to read and maintain. We will try a correct approach to using the if statement.
public class JobEligibilityWithLogicalOperators { public static void main(String[] args) { int age = 30; boolean hasDegree = true; boolean hasExperience = false; if (age >= 25 && hasDegree && hasExperience) { System.out.println("Congratulations! You are eligible for the job."); } else if (age >= 25 && hasDegree) { System.out.println("Sorry, you are not eligible due to lack of experience."); } else if (age >= 25) { System.out.println("Sorry, you are not eligible due to lack of a degree."); } else { System.out.println("Sorry, you are not eligible due to age requirement."); } } }
By use of logical operators (&& and ||) instead of nested if statements, the code is more readable and maintainable. Here we used a single if statement with logical operators to check the eligibility conditions. The logical operators make the code more readable, and concise, reduce redundancy, and ensure that each condition is evaluated independently.
5. Consistent Use of Parentheses
When we use logical operators in the condition of the if statement then use parentheses to group conditions, to ensure the evaluation order is as intended. It ensures the desired evaluation order and prevents ambiguity. Let’s see which type of problem we can face if don’t use parentheses and the see the best practices for if statements
public class IncorrectParentheses { public static void main(String[] args) { int age = 25; boolean hasDegree = true; boolean hasExperience = true; if (age >= 25 && hasDegree || hasExperience) { System.out.println("Congratulations! You are eligible for the job."); } else { System.out.println("Sorry, you are not eligible for the job."); } } }
Here we used the logical operators (&& and ||) to check conditions based on age, degree, and experience. We haven’t used the parentheses to group the conditions properly and that can be the cause of the incorrect results.
See the condition, age >= 25 && hasDegree || hasExperience, here we used AND (&&) and OR (||) operator. First of all, it evaluates age >= 25 && hasDegree because && has higher precedence than ||. As a result, the outcome may not be what we intended.
For example, if age >= 25 is false and hasExperience is true, the overall condition will be true, leading to incorrect eligibility determination.
public class CorrectParentheses { public static void main(String[] args) { int age = 25; boolean hasDegree = true; boolean hasExperience = true; if (age >= 25 && (hasDegree || hasExperience)) { System.out.println("Congratulations! You are eligible for the job."); } else { System.out.println("Sorry, you are not eligible for the job."); } } }
Let’s add parentheses with conditions to group the conditions properly. Now, the || operation is evaluated first due to the higher precedence of parentheses, and then the && operation is evaluated.
By using parentheses to group (hasDegree || hasExperience), it evaluates the || operation first. This way, the eligibility condition is more accurately determined based on whether the applicant has either a degree or relevant experience and is at least 25 years old.
6. Always use null Checks
Always perform a null check before accessing any method or variable of the object to avoid NullPointerException. Whenever you use any method or variable of object then you should perform a null check.
public class IncorrectNullCheck { public static void main(String[] args) { String username = null; if (username.length() > 0) { System.out.println("Username is not empty."); } else { System.out.println("Username is empty or null."); } } }
See the problem area with an example, where we have a variable of String type, username initialized to null. If we don’t perform a null check before accessing the length() method on the username object. It will show NullPointerException at runtime because we are trying to call a method on a null object. See the possible solution.
public class CorrectNullCheck { public static void main(String[] args) { String username = null; if (username != null && username.length() > 0) { System.out.println("Username is not empty."); } else { System.out.println("Username is empty or null."); } } }
In this example, we performed a null check on the username object before accessing its length() method. By using the != null condition, we ensure that we do not attempt to call methods on a null object. Only when the username is not null, we proceed to check its length. This avoids the NullPointerException, and the code can safely handle scenarios where the username might be null.
7. Use Short-Circuiting
We have seen, how we can use logical operators in if statements, and we should use logical operators instead of nested if statements. But we should take advantage of short-circuiting behavior for logical operators (&&, ||).
As we know the about the && and || operators. If the first condition in an && operation evaluates to false, the second condition won’t be evaluated, and similarly, if the first condition in an || operation evaluates to true, the second condition won’t be evaluated.
public class IncorrectShortCircuiting { public static void main(String[] args) { int number = 10; var isGreaterThanFive = isGreaterThanFive(number); var isDivisibleByThree = isDivisibleByThree(number); if (isGreaterThanFive && isDivisibleByThree) { System.out.println("The number is greater than 5 and divisible by 3."); } } public static boolean isGreaterThanFive(int number) { System.out.println("Checking if the number is greater than 5..."); return number > 5; } public static boolean isDivisibleByThree(int number) { System.out.println("Checking if the number is divisible by 3..."); return number % 3 == 0; } }
Here we want to check two conditions, Is the Number Greater than Five? Is the number divisible by 3?
For both conditions, we created two methods isGreaterThanFive and isDivisibleByThree. We use both these methods as conditions for the if statement. However, when the number is not greater than 5, the isDivisibleByThree method is still executed, even though it is not necessary to determine the final outcome of the if statement.
public class CorrectShortCircuiting { public static void main(String[] args) { int number = 3; if (isGreaterThanFive(number) && isDivisibleByThree(number)) { System.out.println("The number is greater than 5 and divisible by 3."); } } public static boolean isGreaterThanFive(int number) { System.out.println("Checking if the number is greater than 5..."); return number > 5; } public static boolean isDivisibleByThree(int number) { System.out.println("Checking if the number is divisible by 3..."); return number % 3 == 0; } }
Now we changed the approach and when the isGreaterThanFive method returns false, the isDivisibleByThree method is not executed because the outcome of the if statement can be determined based on the first operand alone. Thus, unnecessary computation is avoided.
8. Avoid Complex Conditions
Don’t write complex conditions in if conditions, always try to keep it simple and easy to understand. If we have a complex condition, then we should refactor it or break it into small methods so that it can be manageable.
Let’s try to understand it with an example, here we have a complex condition in the if statement that checks the job eligibility based on multiple factors. It contains multiple nested expressions, making it error-prone and challenging to maintain. It can lead to bugs and confusion.
public class IncorrectComplexConditions { public static void main(String[] args) { int age = 30; String city = "New York"; boolean hasDegree = true; boolean hasExperience = true; if ((age >= 25 && age <= 40) && (city.equals("New York") || city.equals("Los Angeles")) && hasDegree && hasExperience) { System.out.println("You are eligible for the job."); } else { System.out.println("Sorry, you are not eligible for the job."); } } }
We have refactored the complex condition into a separate method isEligibleCandidate, where each eligibility criterion is defined in a simpler boolean variable. The isEligibleCandidate method returns the final eligibility status based on the individual criteria.
public class CorrectSimpleConditions { public static void main(String[] args) { int age = 30; String city = "New York"; boolean hasDegree = true; boolean hasExperience = true; if (isEligibleCandidate(age, city, hasDegree, hasExperience)) { System.out.println("You are eligible for the job."); } else { System.out.println("Sorry, you are not eligible for the job."); } } public static boolean isEligibleCandidate(int age, String city, boolean hasDegree, boolean hasExperience) { boolean isAgeEligible = age >= 25 && age <= 40; boolean isLocationEligible = city.equals("New York") || city.equals("Los Angeles"); boolean isDegreeAndExperienceEligible = hasDegree && hasExperience; return isAgeEligible && isLocationEligible && isDegreeAndExperienceEligible; } }
public class CorrectSimpleConditions { public static void main(String[] args) { int age = 30; String city = "New York"; boolean hasDegree = true; boolean hasExperience = true; if (isEligibleCandidate(age, city, hasDegree, hasExperience)) { System.out.println("You are eligible for the job."); } else { System.out.println("Sorry, you are not eligible for the job."); } } public static boolean isEligibleCandidate(int age, String city, boolean hasDegree, boolean hasExperience) { boolean isAgeEligible = age >= 25 && age <= 40; boolean isLocationEligible = city.equals("New York") || city.equals("Los Angeles"); boolean isDegreeAndExperienceEligible = hasDegree && hasExperience; return isAgeEligible && isLocationEligible && isDegreeAndExperienceEligible; } }
9. Use Constants or Enumerations
As a beginner, we use the number or String in the if statement’s condition. But we should use constants or enumerations to make your code more maintainable and less error-prone.
Let’s understand it with one example, here we are using integer numbers (1, 2, and 3) to represent different user types. But these numbers lack context and can make the code less readable and harder to maintain. If someone else reads this code, it might not be clear what each number represents.See the vest practices for if statements
public class IncorrectMagicNumbers { public static void main(String[] args) { int userType = 2; if (userType == 1) { System.out.println("Welcome, Admin!"); } else if (userType == 2) { System.out.println("Welcome, Moderator!"); } else if (userType == 3) { System.out.println("Welcome, User!"); } else { System.out.println("Unknown User Type"); } } }
Let’s define an enumeration UserType, which is more expressive and type-safe compared to using integers or strings. If you are not familiar with Enumerations, you can read it here. It provides clear and self-documenting names for each user type, improving code readability and maintainability.
public class CorrectEnum { // Define an enumeration for user types public enum UserType { ADMIN, MODERATOR, USER } public static void main(String[] args) { UserType userType = UserType.MODERATOR; switch (userType) { case ADMIN: System.out.println("Welcome, Admin!"); break; case MODERATOR: System.out.println("Welcome, Moderator!"); break; case USER: System.out.println("Welcome, User!"); break; default: System.out.println("Unknown User Type"); } } }
10. Use Switch-Case for Multiple Options
Instead of using a ladder if statement, it’s better to use a switch statement. Whenever we have multiple options to check against a single variable, we should consider using the switch-case statement, which can improve code readability. Let’s see the problem area first and then move to the solution with best practices for if statements
import java.util.Scanner; public class IncorrectNestedIf { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Enter a day of the week (1-7): "); int dayOfWeek = scanner.nextInt(); if (dayOfWeek == 1) { System.out.println("Today is Monday."); } else if (dayOfWeek == 2) { System.out.println("Today is Tuesday."); } else if (dayOfWeek == 3) { System.out.println("Today is Wednesday."); } else if (dayOfWeek == 4) { System.out.println("Today is Thursday."); } else if (dayOfWeek == 5) { System.out.println("Today is Friday."); } else if (dayOfWeek == 6) { System.out.println("Today is Saturday."); } else if (dayOfWeek == 7) { System.out.println("Today is Sunday."); } else { System.out.println("Invalid day of the week."); } } }
In this example, we used ladder if statements to check the day of the week based on the value of dayOfWeek. While this approach works, it makes code complex, especially when there are many options to check.
import java.util.Scanner; public class CorrectSwitchCase { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter the day of the week (1-7): "); int dayOfWeek = input.nextInt(); switch (dayOfWeek) { case 1: System.out.println("Today is Monday."); break; case 2: System.out.println("Today is Tuesday."); break; case 3: System.out.println("Today is Wednesday."); break; case 4: System.out.println("Today is Thursday."); break; case 5: System.out.println("Today is Friday."); break; case 6: System.out.println("Today is Saturday."); break; case 7: System.out.println("Today is Sunday."); break; default: System.out.println("Invalid day of the week."); } } }
Here, we used the switch-case statement instead of the ladder if statement to handle the multiple options for the dayOfWeek variable. You can read the advantages of the switch case from here.
Now code is more concise and readable, especially when dealing with many options for a single variable. It also helps to avoid the nesting of if statements, leading to a more straightforward and efficient control flow in the code.