Unraveling Java's Most Common Issue

Unraveling Java's Most Common Issue

Having worked with Java for over 2 years, I've noticed some common issues that, while not too complicated, can be confusing. Inspired by Jonathan, a developer advocate at Sonar, who surveyed SonarLint telemetry for the last 2 years, covering 2.5 million issues, let's dive into these common issues from my work experience, which you might also relate to.

These issues might seem small, but their impact can be significant. Addressing them can result in several benefits:

  • Code will be more readable.

  • Code will increase maintainability.

  • Finding bugs can be easier.

  • Code will be neater and cleaner in our projects.

Ready to dive in? Let's get started!

1. Code Commented out:

  • We should remove commented-out code because it makes readability harder. If the code is needed again, we can retrieve it from Git.

  • Sometimes, it causes confusion for readers, wondering if the code is commented out temporarily or should be removed.

Problem:

public class CommentedCodeExample {

    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        int result = x + y;

        // commented out code
        /*
            System.out.println("Result: " + result);
            System.out.println("This part is commented out for testing purposes.");
        */

        System.out.println("Other logic in the program.");
    }
}

Solution:

public class CommentedCodeExample {

    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        int result = x + y;

        System.out.println("Result: " + result);
        System.out.println("Other logic in the program.");
    }
}

2. "TODO" Tags:

  • Leaving 'TODO' comments in the source code leads to incomplete code that can impact several areas.

  • Some problems include team members being unsure which features will be included in the final release. Second one is not implementing those parts could lead to bugs in the future.

Problem:

public class TodoExample {

    public static void main(String[] args) {
        int x = 10;
        int y = 20;

        // TODO: Implement logic to calculate result

        System.out.println("Other logic in the program.");
    }
}

Solution:

public class TodoExample {

    public static void main(String[] args) {
        int x = 10;
        int y = 20;

        int result = x + y;

        System.out.println("Result: " + result);
        System.out.println("Other logic in the program.");
    }
}

3. String Literals Duplicated:

  • Having duplicate strings will lead to extra work or missing changes when those values need to be adjusted for new conditions.

  • Using constants to store string literals will maintain consistency in the code base.

Problem:

public class DuplicatedStringsExample {

    public static void main(String[] args) {
        System.out.println("Hello");
        System.out.println("Hello");
    }
}

Solution:

public class DuplicatedStringsExample {

    private static final String MESSAGE = "Hello";

    public static void main(String[] args) {
        System.out.println(MESSAGE);
        System.out.println(MESSAGE);
    }
}

4. Unused elements (imports, assignments, variable, private fileds, perameters) should be removed

  • Unused elements reduce the readability of the code, making it harder to identify the intention.

  • Check for unused code and remove what is no longer used.

Problem:

import java.util.List; // import not used

public class UnusedElementsExample {

    private int x = 10; // private field not used

    private int compute(int a, int b) { // b argument not used
        return a * 10;
    }
}

Solution:

public class UnusedElementsExample {

    private int compute(int a) { 
        return a * 10;
    }
}

5. Raw types should not used

  • Generic types without type parameters should not be used, as it avoids type checking of unsafe code during compilation.

  • If you don't want any surprises during runtime, avoid using raw types.

Problem:

import java.util.ArrayList;

public class RawTypesExample {

    public static void main(String[] args) {
        // Raw type used here
        ArrayList myList = new ArrayList();
    }
}

Solution:

import java.util.ArrayList;

public class RawTypesExample {

    public static void main(String[] args) {
        // Parameterized type used here
        ArrayList<String> myList = new ArrayList<>();
    }
}

6. Generic Exceptions Should Never Be Thrown:

  • The usage of generic exceptions prevents calling methods from handling different system-generated exceptions and application-generated errors.

  • Create custom system exceptions to provide callers the ability to decide what to do, ensuring a detailed and differential list of catches.

Problem:

public class GenericExceptionExample {

    public static void main(String[] args) {
        try {
            // Some code that may throw a generic exception
        } catch (Exception e) {
            // Handling the generic exception
        }
    }
}

Solution:

public class CustomException extends Exception {
    // Custom exception class with details and customization
}

public class GenericExceptionExample {

    public static void main(String[] args) throws CustomException {
        try {
            // Some code that may throw a specific exception
            throw new CustomException("Something went wrong!");
        } catch (CustomException e) {
            // Handling the specific exception
            System.out.println("Caught a specific exception: " + e.getMessage());
        }
    }
}

Note: When refactoring code, first modify and commit the existing code to Git without introducing new features. Then, add new code separately to maintain clarity and flexibility in tracking changes.

Your feedback is invaluable! Please take a moment to like and share your thoughts on this post.