Nullable & NonNull

Jintin
3 min readMar 16, 2018

--

What is the the most common crash you face in daily Android development? Probably the NullPointerException!!!

There are lots of discussion about why Null is a bad idea and more modern languages like Swift and Kotlin are trying to get rid of this exception by other approach. But face the truth, you need to add check when you access variable in Java to avoid the NullPointerException.

public void someFunction(A a) {
if (a != null) {
a.doSomething();
}
}
A = new A();
a = null; // you'll never know when will somebody add this.
someFunction(a);

It’s never too aggressive to fight against the NullPointerException, but it’s even better if we can get more context about what the variable will/should be. That’s why Android provides @Nullable and @NonNull to help us distinguish the nullability.

Installation

Simply add dependency in your build.gradle.

implementation 'com.android.support:support-annotations:27.1.0'

Noted if you already use support-v4, you don’t need to add anything because it’s bundle together.

Example

public void someFunction1(@Nullable A a) {
if (a != null) { // warn if we don't add null check
a.doSomething();
}
}
public void someFunction2(@NonNull A a) {
a.doSomething();
}
A a = new A();
a = null;
someFunction1(a);
someFunction2(a); // warn a is null;

If any parameter is mark as @NonNull, we can stripe the null check with confidant. On the other hand, if the parameter is mark as @Nullable and we don’t add null check inside the function, Android Studio will warn you with lint error and some visual hint.

The @NonNull/@Nullable annotation can put in front of functions as well to indicate the return value nullability.

@NonNull
public A getA() {
return null; // warn: ‘null’ is returned by the method ...
}

Easy, right? Keep your Billion Dollar in your pocket and enjoy using this @Nullable and @NonNull.

Kotlin Support

@Nullable and Kotlin are meant for each other since Kotlin natively support Optional, @Nullable variable will be treat as Optional automatically on Kotlin side.

// Java
@Nullable
public ActionBar getSupportActionBar() {
......
}
//Kotlin
supportActionBar?.setTitle(R.string.some_title)

Library Support

Some common library like AutoValue, ButterKnife will respect the @Nullable annotation and generate correspond nullable code.

If you like, you can take 3 min to see what is AutoValue and ButterKnife.

Other Annotations

Beside @Nullable and @NonNull, there are other helpful annotations to verify the scope of variable.

1. Resource annotations

(LayoutRes, AnimRes, ColorRes, DimenRes, ArrayRes, BoolRes, StringRes…)

This kind of annotations will restrict the incoming parameter type belong certain type.

public void setTitle(@StringRes int resId) { … }setTitle(R.color.white); //warn

2. Thread annotations

(MainThread, UiThread, WorkerThread, BinderThread, AnyThread)

This kind of annotations will restrict method should execute on desire Thread.

@MainThread
public void hideView(){
titleView.setVisibility(View.Gone);
}

3. Value constraint annotations

(IntRange, FloatRange, Size…)

Can restrict the value in certain range, like:

public void setScore(@IntRange(from = 0, to = 100) int score) {...}

4. Permission Annotations

One of the complex scenario since the born of Runtime permission. The @RequiresPermission is here to rescue. There are three type of usage.

The basic example, just put the permission name inside RequiresPermission:

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public void setWallpaper(Bitmap bitmap) {...}

@RequiresPermission(ACCESS_COARSE_LOCATION)
public Location getLastKnownLocation(String provider) {...}

If you want to have multiple permissions or one of some permissions you can use allOf or anyOf:

@RequiresPermission(allOf = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE})
public static final void copyFile(String dest, String source) {...}
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public abstract Location getLastKnownLocation(String provider);

Another case is for Intent which might need some particular permission, we can put the RequiresPermission at the action string.

@RequiresPermission(Manifest.permission.CALL_PHONE)
public static final String ACTION_CALL = "...";
Intent intent = new Intent(Intent.ACTION_CALL);
startActivity(intent); // will warn you check permission first.

Epilogue

Support annotation is not a tool to cure all exception, but it makes us more stronger than nothing. Although it require some additional effort, it worth it.

Reference

--

--

Jintin

Android/iOS developer, husband and dad. Love to build interesting things to make life easier.