xtratic
xtratic

Reputation: 4699

Validated variable null warning

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.

Upvotes: 1

Views: 749

Answers (2)

mernst
mernst

Reputation: 8147

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 ...
        }
    }
}

Upvotes: 1

Victor Stafusa
Victor Stafusa

Reputation: 14613

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 ...
        }
    }
}

Upvotes: 1

Related Questions