Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: NullPointerException despite @NonNull

I'm using annotations to ensure that parameter will be not null, assuming this would cause compiler check.

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

    return accounts.get(key);
}

However, by running this code I get NullPointerException exactly on this line

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

What is the point of annotations then?

Even more obscuring, if I write additional check like this

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

Android Studio warns me that the check is useless!

Update: full call stack:

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)
like image 314
Arnie Schwarzvogel Avatar asked Jan 05 '23 13:01

Arnie Schwarzvogel


1 Answers

Annotations create a contract. @NonNull says that this method does not accept null and passing it may crash the program (which is exactly what happened). Android Studio will warn about any uses of this method where it can deduce that null may be passed. But it won't prevent passing a null.

Very similar contract is parameter to [] for an array which cannot be outside of its bounds (e.g. smaller than 0) but that still won't prevent a developer to pass a value outside of the bounds.

The check for null is marked as superfluous because by the contract, there should never be null. If you add the check, the method will now know how to handle null and therefore should not be marked as @NonNull.

like image 77
StenSoft Avatar answered Jan 08 '23 01:01

StenSoft