Каков наилучший способ выполнить нулевую проверку БЕЗ генерируемого исключения NullPointerException? - PullRequest
0 голосов
/ 02 августа 2011

Итак, я знаю, что МОЖЕТ иметь нулевой список (в частности, ArrayList). Теперь простая проверка для этого на самом деле генерирует исключение NullPointerException, когда я уже проверяю это. Это меня озадачило, так как я всегда использовал его успешно, но я уверен, что что-то упустил:

public class MyPost {

private int id;

private List<Label> labels;

public MyPost(int id){ this.id = id }

//getter setters for both plus this added method:

public void addLabel(Label aLabel)
  {
     if(labels == null)
       labels = new ArrayList<Label>();

     labels.add(aLabel);
  }

}

Теперь в другой части моего кода я перебираю список идентификаторов, отправленных клиентом. Для простоты предположим, что переменная цикла 'i' предоставляет идентификаторы

MyPost aPost = new MyPost(i);

В моей логике я могу добавлять или не добавлять ярлыки к сообщению. Поэтому в конце, прежде чем продолжить, я проверяю наличие таких ярлыков:

if(aPost.getLabels()!=null)
   //process labels

Теперь это вызывает исключение нулевого указателя, если ничего не было добавлено в список меток! Но это именно то, что я хочу проверить, и я все еще получаю NPE !!!

Я знаю, что aPost.getLabels () равен нулю, если к нему ничего не добавлено. Но сравнение кажется неудачным и выбрасывает NPE. Как решить эту проблему? Просто поставил меня в тупик!

UPDATE: Вот код получения метки. Просто банальный добытчик ...

public List<Label> getLabels() { return labels;}

Мы заметили кое-что, что мы упустили раньше. Я уверен, что java использовал для «короткого замыкания» это условия if, то есть в условии ИЛИ, если первое условие оценивается как истинное, оно не проверяет второе (аналогичное короткое замыкание для И, если первое условие оценивается как ложное ). Я не совсем уверен, является ли это причиной, но вот предложение if:

if(aPost.getLabels()!=null || !aPost.getLabels().isEmpty())
//process labels

Если список действительно нулевой, короткое замыкание не должно оценивать второе условие, исправить ?? Кажется, что это может быть причиной, но мы все еще проверяем это. Пока только догадка ...

Ответы [ 3 ]

10 голосов
/ 02 августа 2011

Как правило, первое, что вы делаете, когда отлаживаете NPE, - это внимательно исследует трассировку стека и идентифицируете точную линию, из которой она выброшена.

Следующим шагом является проверка всех значений, которые слева оператора разыменования (.) в этой строке.Другим источником NPE является новый вид циклов for, а третий - автоматическая распаковка. Насколько я знаю, нет других конструкций, которые по своей сути генерируют NPE, хотя, конечно, всегда может быть код, который выбрасываетэто явно.

Все это означает, что без трассировки стека и полного кода мы можем только догадываться.

(В качестве альтернативы, если вы используете IDE, вы можете просто установить точку останова исключенияи изучите значения времени выполнения ваших переменных в момент выброса NPE. Но вы должны быть в состоянии найти NPE путем автономного анализа кода и трассировки стека. Это важный навык.)

Обновление : Глядя на обновленный вопрос, очевидно, что утверждение if неверно.Он должен выглядеть следующим образом:

if(aPost.getLabels()!=null && !aPost.getLabels().isEmpty())
//process labels

ИЛИ там не правильная операция, так как вы хотите, чтобы aPost.getLabels() был не нулевым И не пустым.Java действительно останавливает вычисление логических выражений, как только значение известно, но в исходном выражении это не имело место, если aPost.getLabels() был нулевым.

3 голосов
/ 02 августа 2011

Лучшим способом кодирования было бы всегда инициализировать массив, и вместо проверки нуля просто выполнить итерацию по списку, который в пустом случае ничего бы не сделал, потому что он пустой (не ноль).

public class MyPost {
    private int id;
    private List<Label> labels = new ArrayList<Label>;
    public MyPost(int id){ this.id = id }

    //getter setters for both plus this added method:
    public void addLabel(Label aLabel) {
        labels.add(aLabel);
    }
}

// then later...
public void someProcessing() {
    for (Label label: labels) {
        // process label here
    }
}

Теперь у вас больше нет NPE, и вам не нужно иметь неприятно выглядящий код проверки на ноль, вы просто полагаетесь на тот факт, что пустой список не будет повторяться.

В качестве альтернативы (как я уже сказал)в моих комментариях) если вам нужно лениво создавать экземпляр List, делайте это, но всегда возвращайте действительный объект List, который можно повторять, изменяя getLabels () на

public List<Label> getLabels() {
    return labels == null ? Collections.emptyList() : labels
}

Это означает, что ни один метод, которыйДля вызовов getLabels () всегда требуется проверка на наличие нулевого объекта, они могут просто перебирать возвращаемый объект, что, по-видимому, в конечном итоге и будет происходить со списком.Это действительно повышает надежность кода и удобочитаемость.Я должен рассмотреть много кода, который использует этот метод пояса и скобок - всегда проверять наличие нулей перед доступом к объектам, которые можно очистить, обеспечив возвращаемый объект, действующий как имя, вместо того, чтобы быть потенциально нулевым.

РЕДАКТИРОВАТЬ: удалить раздел о getLabels () после того, как OP обновил сообщение о фактическом используемом операторе if, и добавил комментарии о том, как сделать список действующим как список.

1 голос
/ 02 августа 2011

Вы на самом деле что-то упускаете, хотя вы очень близки к тому, что вы хотите:

исключение пустого указателя выдается в выражении вида

A.method()

A.field

если A равно нулю. Это означает, что в утверждении типа

a.b.c.d.e().f.g 

Выдается исключение нулевого указателя, если a равно null или a.b равно null или a.b.c равно null, и т. Д.

Так что в вашем примере, если вы получаете исключение при выполнении

if(aPost.getLabels()!=null)

единственное решение состоит в том, что aPost имеет значение null. Ничего другого.

И на самом деле вы правы, чтобы узнать, является ли что-то нулевым, хорошо сравнить его с нулевым, используя знак равенства (==)

Добавьте в свой код следующее:

if( aPost == null )
   System.out.println( "Oh, aPost is null itself and my bug is not related to its fields being null." );

С уважением, Stéphane

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...