Reputation: 4699
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
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
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