Skip to content
ichigotake edited this page Apr 20, 2014 · 7 revisions

Android開発では何も考えずに実装していると NullPointerException が頻発するリスクがとても高いです

AndroidADKにおける Null

public onActivityCreated(Bundle savedInstanceState) {
    super.onActivitiCreated(savedInstanceState);

    View someView = view.findViewById(R.id.some_view); //該当するビューがない場合nullが返る

    //Fragment,Activityにおいて、Bundle型の引数はオブジェクトそのものが Null である事を考慮して実装しなければいけない
    //他にも「存在しなければNullを返す」メソッドは数多く存在するため、クラス概要が記述されているドキュメントを読んで確認するべきである
    //これを回避するためにBundlを適切に扱うクラスでラップなどをするべき
    if (savedInstanceState != null
        && savedInstanceState.containsKey("key_string")
        ) {
        
        //もしif文の条件式のように( != null)のチェックをせずにいると、ここでNullPointerExceptionが発生
        String name = savedInstanceState.getString("key_string");
    }
}

Optional型

OptionalクラスはGoogleの配布する guavaライブラリ に同梱されている

Optional型を使うと null == object というチェックを object.isPresent() という具合にオブジェクト指向らしくメソッドでチェックできるようになり、 受け取り側にOptional型を意識させる事でNullPointerExceptionの発生リスクを抑えられる

環境が許すのであれば、Android開発のわずわらしさを考えるとguavaライブラリは導入する価値はあるかもしれない

※ guavaには他にも便利クラス群が大量にあるので、とりあえず入れておけば後でお世話になる....事もあるかもしれない

※ Optionalのためだけにguavaを利用するには 大きすぎたりProguardが大変だったり といろいろあるので、本家からOptionalのみを抽出してみた

// Optionalのサンプル

Optional<String> name = getName();

final TextView genderView = getNameView(); //便箋上省略

if (gender.isPresent()) {
    // nameの中身が *Null* ではない時
    genderView.setText(gender.get()); //Optional内の本来の値は get()で取得
} else {
    genderView.setText("非公開");
}

// もしくはorを使う(上の例と同じ動作
//genderView.set(gender.or("非公開"));

class OptionalSample { // 説明上必要な箇所のみ記載
    
    private Optional<String> mGender = Optional.absent();
    
    Optional<String> getGender() {
        return mGender;
    }

    void setName(String gender) {
        mGender = Optional.fromNullable(gender);
    }

}

ただし、全てをOptionalにして Optional.isPresent() を利用すればよいかと言うとそうではない。オブジェクトの扱いの手間が無駄に増えてしまうだけで得策ではない。 そもそも Optional はその単語の意味通り、「必須ではないけどオプションとして追加設定が出来る」ようなものに対して利用する事が望ましい

あくまで設計の原理的な考え方として、Nullではない事が約束されているようなものはOptionalにしない、と考える 必須な値はコンストラクタに含めるようにするか、もしくはFactoryメソッドでラップするようにするべきである

コンストラクタで引数を受けとらずに後からセットする値の場合、後にゲッターで取得する際に中身がNullである事は十分考えられる そういった場合にOptional型を使うとよいだろう

class OptionalSample {

    final private String mName;

    private Optional<String> mGender = Optional.absent();

    OptionalSample(String name) {
        mName = name;
    }

    //コンストラクタでnameがセットされる事が確約されているのでOptionalにはしない
    //そもそもここが Null であるならば、OptionalSampleのインスタンス生成前の段階で入力チェックをするべき
    String getName() {
        return mName;
    }

    //mGenderはコンストラクタではセットしない値なのでnullである可能性があるのでOptional型にする

    void setGender(String gender) {
        mGender = Optional.fromNullable(gender);
    }

    Optional<String> getGender() {
        return mGender;
    }
}
Clone this wiki locally