1

I'm working through an Android app I'm writing and attempting to bring my code fully in line with Android Studio's lint recommendations.

I have the following code that is issuing a warning (some code is omitted):

final EditText input = (EditText)view.findViewById(R.id.edit_text);
Button button        = (Button)view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String value = input.getText().toString();
        if (value == null || value.length() == 0) {

Android Studio is giving me a warning that:

Condition 'value == null' is always false.

When I allow Android Studio to "fix" the problem for me, it recommends:

Simplify 'value == null' to false

The code then becomes:

if (value.length() == 0) {

I've looked through the Android source code (http://www.grepcode.com) and I'm confused. The documentation for EditText says "EditText is a thin veneer over TextView that configures itself to be editable." Then, the getText() method is defined as follows:

@Override
public Editable getText() {
    return (Editable) super.getText();
}

When I go to getText() for a TextView (the "super"), I see this:

public CharSequence getText() {
    return mText;
}

The setText() method for a TextView appears to disallow a null value, because this is the start of that method:

private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) {
    if (text == null) {
        text = "";
    }

The default constructor starts this way, too:

public TextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    mText = "";

So, it would appear that there is no way for getText() to return a null value, but the comments on this answer indicate that it is. The answers to this question also seem to indicate that it is possible.

I'd like to practice defensive coding, which is why I structured my code the way I did from the beginning, but I don't want to be doing a null check for something that cannot possibly be null. So, what's the best practice in this case?

Community
  • 1
  • 1
mbm29414
  • 11,558
  • 6
  • 56
  • 87
  • you will check like value.equals("") and refers this also http://stackoverflow.com/a/3321548/4478125 – Aman Jham Jul 28 '15 at 19:27
  • I think the lint check is telling you something slightly differently than what you're reading from it. It actually doesn't matter for this if-statement whether or not `EditText.getText()` can ever return null. Because if it returns null, then the `.toString()` will have caused a NullPointerException before the if-statement is even reached. Therefore, if the if-statement is at all reachable, value cannot have been null. Therefore, the `value == null` check is not needed. – Barend Jul 28 '15 at 19:28
  • @Barend That makes a little more sense, then. I was figuring that the lint check couldn't be smart enough to analyze through all the layers that I did. So, then, the question becomes whether my line of `String value = input.getText().toString();` is vulnerable for an NPE. – mbm29414 Jul 28 '15 at 19:29
  • If you simplify `input.getText().toString()` to `null.toString()`, you can easily see that it will NPE. I'm not sure if `getText()` can ever return null, and even if the standard `EditText` can't then maybe some unknown subclass of `EditText` can, so the defensive approach is to rewrite the code in a completely null-safe way. – Barend Jul 28 '15 at 19:31
  • Based on details you have given by digging into source code, `getText` will not return null. The two SO links you posted in your question does not really say that getText() returned null. – Wand Maker Jul 28 '15 at 19:34
  • @Barend I have updated my code to use this methodology (first getting then `Editable` value and checking that for `null`). Will you put your comments into an answer (including the "maybe some unknown subclass..." part)? – mbm29414 Jul 28 '15 at 19:35
  • @mbm29414 done, thanks – Barend Jul 28 '15 at 19:38
  • see `Editable t = mEditableFactory.newEditable(text);` in `setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen)` – pskink Jul 28 '15 at 19:40

4 Answers4

3

I think the lint check is telling you something slightly differently:

String value = input.getText().toString();
if (value == null || value.length() == 0) {

Condition 'value == null' is always false.

It actually doesn't matter in this if-statement whether or not EditText.getText() can ever return null. Because if it returns null, then the .toString() will have caused a NullPointerException before the if-statement is even reached. Therefore, if the if-statement is at all reachable, value cannot be null.

You can dig all through the Android source code, and maybe you will find that there's no possible way for EditText to return null. If that is the case, then you can just ignore the lint warning if you know that the standard EditText is in your app.

However, in the more general case, nothing is stopping me from sticking this in the view hierarchy:

public class EvilEditText extends EditText {
    // Constructors

    @Override
    public Editable getText() {
        return null;
    }
}

so the safest option is to rewrite your code to be completely nullsafe:

Editable e = input.getText();
String value = (e == null ? null : e.toString());
if (TextUtils.isEmpty(value)) {  //hat tip to Deepak Goyal's answer. Could also use isEmpty(e)
Barend
  • 17,296
  • 2
  • 61
  • 80
  • Great answer. Thanks! What's the difference between `value.isEmpty()` and `TextUtils.isEmpty(value)`? – mbm29414 Jul 28 '15 at 19:42
  • I think neither `Editable` nor `String` has an `isEmpty()` method, so the first would not compile? – Barend Jul 28 '15 at 19:46
  • According to developer.android.com, it does. Here's the description: "Returns true if the length of this string is 0." Not sure how that differs from `TextUtils`. Just FYI. ;-) – mbm29414 Jul 28 '15 at 19:54
  • Huh. I started doing Java around 1.2. I never realized the Java String got an `isEmpty()` method in the 1.6 release. Today I learned… :) – Barend Jul 28 '15 at 20:05
  • Huzzah! Glad I could help you after you helped me! – mbm29414 Jul 28 '15 at 20:06
  • if value is null, value.isEmpty() would cause NPE. It its better to use TextUtils.isEmpty() – Purushothaman Ramraj Aug 11 '16 at 15:04
1

You can use TextUtils.isEmpty(value);

final EditText input = (EditText)view.findViewById(R.id.edit_text);
Button button        = (Button)view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String value = input.getText().toString();
        if (TextUtils.isEmpty(value)) {
          // value is empty
        }else{
          // have value
        }
Deepak Goyal
  • 4,747
  • 2
  • 21
  • 46
  • 2
    That doesn't really answer my question, though. It would seem that you have no concern about an NPE with your code `input.getText().toString`, right? So, then, either `getText()` always returns a non-null value, or `toString()` can take a null value, which I don't believe is true. Do you see what I'm asking? – mbm29414 Jul 28 '15 at 19:28
  • This will still NPE if `input.getText()` ever returns null. – Barend Jul 28 '15 at 19:28
  • no it will not return a null value, it can return you a empty string. – Deepak Goyal Jul 28 '15 at 19:30
1

You are likely receiving this warning because your value does not hold what is returned from EditText#getText() (which returns a CharSequence), it instead holds the return from CharSequence#toString() (which returns a String).

It should be impossible for a toString() call on a non-null CharSequence to return null, this is why lint warns you that the null check is unnecessary. You should do the null check on the return of EditText#getText() instead.

In other words, if your input.getText() was null, it would already fail without getting to call toString(), hence your null check would never be reached.

ugo
  • 2,705
  • 2
  • 30
  • 34
0

The value in the expression

String value = input.getText().toString();

will not be null. Since if input is null it will generetae an NPE for the method call input.getText(). and hence the next statement won't execute. If you handles this NPE somthing like

            String value = null;
            if (input!=null) {
                value = input.getText().toString();
            }
            if (value == null || value.length() == 0){

            }

Then you won't get that Indication.

Sanjeet A
  • 5,171
  • 3
  • 23
  • 40