3

Я использую аннотации, чтобы гарантировать, что параметр будет не нулевым, если предположить, что это вызовет проверку компилятора.Android: NullPointerException несмотря на @NonNull

public @Nullable ApplicationAccount accountForKey(@NonNull String key) { 

    return accounts.get(key); 
} 

Однако, запустив этот код я получаю NullPointerException именно на этой линии

java.util.concurrent.ConcurrentHashMap.get (ConcurrentHashMap.java:883) 

Какой смысл аннотаций тогда?

Еще более затемнение, если я пишу дополнительные проверки, как этот

return key!=null?accounts.get(key):null; 

Android Studio предупреждает меня, что чек бесполезно!

Update: полный стек вызовов:

Caused by java.lang.NullPointerException 
     at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:883) 
     at co.getcontrol.services.MerchantCenter.accountForKey(MerchantCenter.java:72) 
     at co.getcontrol.model.customers.CustomersAggregator.loadCustomerDetails(CustomersAggregator.java:91) 
     at co.getcontrol.model.customers.CustomerDetailsPresenter.callData(CustomerDetailsPresenter.java:39) 
     at co.getcontrol.reskin.ui.customers.CustomerDetailsViewFragment.onCreateView(CustomerDetailsViewFragment.java:152) 
     at android.support.v4.app.Fragment.performCreateView(Fragment.java:1974) 
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067) 
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1252) 
     at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738) 
     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1617) 
     at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:339) 
     at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:602) 
     at co.getcontrol.ui.ControlActivity.onStart(ControlActivity.java:13) 
     at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1174) 
     at android.app.Activity.performStart(Activity.java:5353) 
     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2352) 
     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2441) 
     at android.app.ActivityThread.access$900(ActivityThread.java:151) 
     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354) 
     at android.os.Handler.dispatchMessage(Handler.java:110) 
     at android.os.Looper.loop(Looper.java:193) 
     at android.app.ActivityThread.main(ActivityThread.java:5345) 
     at java.lang.reflect.Method.invokeNative(Method.java) 
     at java.lang.reflect.Method.invoke(Method.java:515) 
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828) 
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644) 
     at dalvik.system.NativeStart.main(NativeStart.java) 
+5

время компиляции и выполнения разные вещи ... Это @NonNull не будет волшебно предотвратить нулевое значение, так как вы только что видели ...И вот почему мне не нравятся эти аннотации. – ppeterka

+0

Хм, для меня это исключает весь смысл использования аннотаций. Без аннотации я теперь должен заботиться о проверке указателя. Если я напишу @NonNull, то я автоматически делегирую эту обязанность кому-то (компилятор). Я ожидаю тогда, по крайней мере, тот код, который * использует * этот метод, должен вызывать ошибку компиляции/предупреждение, когда он не проверяет значение null. –

+0

Есть полезные аннотации. Это бесполезно. И yep, вы должны проверить свои ссылки (нет указателей на Java!) ... И вы не можете делегировать это во время компиляции! Не могу. Невозможно. Подумайте об этом: вы пишете лучшую JSON-библиотеку в мире, компилируете ее, выпускаете, а кто-то еще называет ее большим толстым нулем. Как мог компилятор поймать это? – ppeterka

ответ

0

Если вы аннотировать @NonNull вы заявляете, что вход не будет null. Это не помешает тупому разработчику передать нуль.

Я хотел бы рассмотреть вопрос о добавлении guava checkNonNull вместо:

public @Nullable ApplicationAccount accountForKey(@NonNull String key) { 
    Preconditions.checkNotNull(key, "Illegal Argument passed: key is Null."); 
    return accounts.get(key); 
} 

Это позволит вам успешно прохождение null методу accounts#get.

В вашем случае это выглядит как ваш accounts is null. Исследуйте это!

5

Аннотации создают договор . @NonNull говорит, что этот метод не принимает null, и его передача может привести к сбою программы (именно это и произошло). Android Studio будет предупреждать о любых применениях этого метода, где он может вывести, что null может быть передан. Но это не помешает пропустить null.

Очень похожий контракт - это параметр [] для массива, который не может находиться за пределами его границ (например, меньше 0), но это все равно не помешает разработчику передать значение за пределами границ.

чек на null отмечен как лишний, так как по договору не должно быть null. Если вы добавите чек, метод теперь будет знать, как обращаться с null и поэтому не должен быть помечен как @NonNull.

1

Рассмотрите возможность использования Lombok library с Android Studio plugin для обеспечения поддержки обоих: предупреждений и исключений во время выполнения при запуске метода только с аннотирующим параметром с помощью @NonNull аннотации lombok.

Это будет выглядеть

import lombok.NonNull; 

public class NonNullExample extends Something { 
    private String name; 

    public NonNullExample(@NonNull Person person) { 
     super("Hello"); 
     this.name = person.getName(); 
    } 
} 

По сравнению с реализацией коробчатого Java:

public class NonNullExample extends Something { 
    private String name; 

    public NonNullExample(@NonNull Person person) { 
     super("Hello"); 
     if (person == null) { 
      throw new NullPointerException("person"); 
     } 
     this.name = person.getName(); 
    } 
} 
Смежные вопросы