1

In Eclipse (4.7.2) with null analysis -> potential null access set to give me a warning.

Given the following code:

public class Test {

    // validator method
    static boolean hasText(String s) {
        return !(s == null || s.trim().isEmpty());
    }

    public static void main(String[] args) {
        // s could come from anywhere and is null iff the data does not exist
        String s = (new Random().nextBoolean()) ? "valid" : null;
        if (hasText(s)) {
            // Potential null pointer access: The variable s may be null at this location
            System.out.println(s.length());
            // ... do actual stuff ...
        }
    }
}

how can I avoid the potential null warning? @NotNull won't work since null is a valid input and the output is a boolean.

Is there a way to tell the compiler that if this validation method returns true then the validated value was non-null?

Is there some better way to handle validation methods like this?

Thanks.


Update for clarity:

The data comes from user input (from xml or .properties file) and will be null iff the data does exist.

Never producing null (eg. setting it to "") would be inventing data that does not exist, and I can't exactly have a NullString object (can't extend String) to represent non-existent data.

hasText(String s) must be able to accept any of this input data and thus must be able to accept null.

6
  • I can't understand what do you want. I mean, your parameter of hasText is a String - not a null. And you're trying to pass the null instead of String calling String s = (new Random().nextBoolean()) ? "valid" : null; Try to write String s = (new Random().nextBoolean()) ? "valid" : ""; Commented Feb 21, 2018 at 20:30
  • @zlakad the String may be null if the data did not exist, the data could come from a variety of sources and I can't exactly create a NullString object to prevent the string from ever being nullable. Commented Feb 21, 2018 at 21:27
  • 2
    O.K. Why don't you use an Optional<String> mayBeNull;? Commented Feb 21, 2018 at 21:34
  • FYI, (s == null || s.trim().isEmpty()) ? false : true is just longhand for !(s == null || s.trim().isEmpty()), or s != null && !s.trim().isEmpty(). Commented Feb 21, 2018 at 21:55
  • @shmosel Whoops. I had just compressed a larger if-else into that ternary operation and it slipped my mind that I should've just simplified it into a boolean expression. Good catch. Commented Feb 21, 2018 at 22:09

2 Answers 2

1

Your code is safe (it never throws a null pointer exception), but perhaps Eclipse's analysis is too weak to establish that. You should consider using a more powerful tool.

The Checker Framework's Nullness Checker can prove your code is safe. You just need to express the contract of hasText: hasText takes a possibly-null argument, and it returns true only if its argument is non-null.

Here is how to express that:

@EnsuresNonNullIf(expression="#1", result=true)
static boolean hasText(@Nullable String s) { ... }

(For more details, see the Javadoc for @EnsuresNonNullIf.)

Here is your full example, which the Nullness Checker verifies without warnings:

import java.util.Random;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;

public class Test {

    // validator method
    @EnsuresNonNullIf(expression="#1", result=true)
    static boolean hasText(@Nullable String s) {
        return !(s == null || s.trim().isEmpty());
    }

    public static void main(String[] args) {
        // s could come from anywhere and is null iff the data does not exist
        String s = (new Random().nextBoolean()) ? "valid" : null;
        if (hasText(s)) {
            // Potential null pointer access: The variable s may be null at this location
            System.out.println(s.length());
            // ... do actual stuff ...
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! This is the type of solution I was looking for.
1

How about this?

public class Test {

    // validator method
    private static boolean hasText(String s) {
        return s != null && !s.trim().isEmpty();
    }

    public static void main(String[] args) {
        String s = (new Random().nextBoolean()) ? "valid" : null;
        if (s != null && hasText(s)) {
            System.out.println(s.length());
            // ... do actual stuff ...
        }
    }
}

With this, you could simplify the hasText(String) method to:

    // validator method
    private static boolean hasText(String s) {
        return !s.trim().isEmpty();
    }

Another alternative would be to just avoid ever producing a null value:

public class Test {

    // validator method
    private static boolean hasText(String s) {
        return !s.trim().isEmpty();
    }

    public static void main(String[] args) {
        String s = (new Random().nextBoolean()) ? "valid" : "";
        if (hasText(s)) {
            System.out.println(s.length());
            // ... do actual stuff ...
        }
    }
}

1 Comment

I've updated my question for better clarity. Unfortunately I don't see a way to avoid the data being able to be null, and thus hasText() must be able to accept null. This validator method is used in many places so it would be somewhat difficult to bring the null check outside of it but either that or ignoring the warning may be my only option...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.