*追記2:まずは int 型じゃねーよ、enumだろって vvakameさんに怒られたけど、もともとは、とあるプロジェクトでサーバーに意図しない値がきてるんだけど、、、みたいなことがあって、サーバーに渡す値を静的に制限するにはどうするのがいいのかな、というのが出発点だったのです。なんで最初が int かっつーと、そのときのコードが int だったからだよっ(つまり初心者がやりがちってこと)
例えば T シャツのサイズをユーザーに選択してもらう画面があったとします。
Tシャツのサイズは L, M, S で、サーバーに投げるときはそれぞれ int値 の 1, 2, 3 として渡します。
まずは int 型で、ってなりますよね。
- private int size;
- @Override
- public void onSizeSelected(int size) {
- this.size = size;
- }
- @OnClick(R.id.send_button)
- void onSendButtonClicked() {
- send(size);
- }
- @POST("/tshirt-size")
- boolean send(@Query("size") int size);
そのためには未選択状態の値を定義しないといけません。よくあるのは -1 で、こんな感じになるでしょう。
- private int size = -1;
- @Override
- public void onSizeSelected(int size) {
- this.size = size;
- }
- @OnClick(R.id.send_button)
- void onSendButtonClicked() {
- if (size == -1) {
- Toast.makeText(context, "サイズを選択してください", Toast.LENGTH_SHORT).show();
- } else {
- send(size);
- }
- }
そこで @IntDef を使って次のようにしてみます。
- public static final int SIZE_L = 1;
- public static final int SIZE_M = 2;
- public static final int SIZE_S = 3;
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SIZE_L, SIZE_M, SIZE_S})
- public @interface ValidSize {
- }
- private int size = -1;
- @Override
- public void onSizeSelected(@ValidSize int size) {
- this.size = size;
- }
- @OnClick(R.id.send_button)
- void onSendButtonClicked() {
- if (size == -1) {
- Toast.makeText(this, "サイズを選択してください", Toast.LENGTH_SHORT).show();
- } else {
- send(size); // ここでエラーになる
- }
- }
- @POST("/tshirt-size")
- boolean send(@ValidSize int size) {
- return true;
- }
次のように値をチェックすればエラーは出なくなりますが、int から int 変換ですしどうもいまいちです。
- @OnClick(R.id.send_button)
- void onSendButtonClicked() {
- if (size == -1) {
- Toast.makeText(this, "サイズを選択してください", Toast.LENGTH_SHORT).show();
- } else {
- switch (size) {
- case SIZE_L:
- send(SIZE_L);
- break;
- case SIZE_M:
- send(SIZE_M);
- break;
- case SIZE_S:
- send(SIZE_S);
- break;
- }
- }
- }
未設定かどうかを保持する boolean 値も持たせます。
- private static class Size {
- public static final int SIZE_L = 1;
- public static final int SIZE_M = 2;
- public static final int SIZE_S = 3;
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SIZE_L, SIZE_M, SIZE_S})
- public @interface ValidSize {
- }
- @ValidSize
- private int size;
- private boolean isValid = false;
- public void setSize(@ValidSize int size) {
- this.size = size;
- this.isValid = true;
- }
- @ValidSize
- public int getSize() {
- return size;
- }
- public boolean isValid() {
- return isValid;
- }
- }
- private Size size = new Size();
- @Override
- public void onSizeSelected(@Size.ValidSize int size) {
- this.size.setSize(size);
- }
- @OnClick(R.id.send_button)
- void onSendButtonClicked() {
- // ここのチェックを強制できない
- if (!size.isValid()) {
- Toast.makeText(this, "サイズを選択してください", Toast.LENGTH_SHORT).show();
- } else {
- send(size.getSize());
- }
- }
- @POST("/tshirt-size")
- boolean send(@Size.ValidSize int size) {
- return true;
- }
size.isValid() でチェックすることを利用側に強制できません。
そこで、null を未設定状態として扱うようにしてみます。
- private static class Size {
- public static final int SIZE_L = 1;
- public static final int SIZE_M = 2;
- public static final int SIZE_S = 3;
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SIZE_L, SIZE_M, SIZE_S})
- public @interface ValidSize {
- }
- public static Size valueOf(@ValidSize int size) {
- return new Size(size);
- }
- @ValidSize
- private final int size;
- private Size(@ValidSize int size) {
- this.size = size;
- }
- @ValidSize
- public int getValue() {
- return size;
- }
- }
- @Nullable
- private Size size = null;
- @Override
- public void onSizeSelected(@Size.ValidSize int size) {
- this.size = Size.valueOf(size);
- }
- @Override
- public void onSizeCleared() {
- this.size = null;
- }
- @OnClick(R.id.send_button)
- void onSendButtonClicked() {
- if (size == null) {
- Toast.makeText(this, "サイズを選択してください", Toast.LENGTH_SHORT).show();
- } else {
- send(size.getValue());
- }
- }
- @POST("/tshirt-size")
- boolean send(@Size.ValidSize int size) {
- return true;
- }
size に @Nullable をつければ、null チェックをしないで size.getValue() を呼び出そうとしたところで Lint の警告が出てくれます。
size に null を代入することでサイズ選択のクリアもできます。
未選択状態に特定の値を割り当てる場合、その値が絶対使われないならいいのですが、使われる場合もありえます。 例えば、透明度を含む色を選択してもらいたい場合では #ffffffff が -1 なので、-1を未選択状態に割り当てるのは不適当になります。
null を未選択状態に割り当てる方法はこういう場合にも適用できます。
追記 :
@vvakame から enum 使えよおらーって言われたので、@zaki50 さんの提案をもとに enum版も載せておきます。
前提として、send()に渡される引数の値を制限したいというのが目的です。 retrofit で引数のパラメータに enum を渡すと toString() の値が利用されるようで、なにかしらコンバーターを仕込まないといけなさそうです。 なので、send() に渡す値に @ValidSize をつけるのはそのままにしたいと思います。
こんな感じになります。ほぼ同じですね。
違いは、onSizeSelected() の引数が @ValidSize int size から Size になったので、この部分の値の制限が Lint からコンパイラになったという点と、 enum なので == で比較できるという点くらいでしょうか。
ちなみにこの書き方だと SIZE_L などを static import する必要があります。Ctrl + space2回 で候補がでます。
- public enum Size {
- L(SIZE_L), M(SIZE_M), S(SIZE_S);
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SIZE_L, SIZE_M, SIZE_S})
- public @interface ValidSize {
- int SIZE_L = 1;
- int SIZE_M = 2;
- int SIZE_S = 3;
- }
- @ValidSize
- private final int size;
- Size(@ValidSize int size) {
- this.size = size;
- }
- @ValidSize
- public int getValue() {
- return size;
- }
- }
- @Nullable
- private Size size = null;
- @Override
- public void onSizeSelected(Size size) {
- this.size = size;
- }
- @Override
- public void onSizeCleared() {
- this.size = null;
- }
- @OnClick(R.id.send_button)
- void onSendButtonClicked() {
- if (size == null) {
- Toast.makeText(this, "サイズを選択してください", Toast.LENGTH_SHORT).show();
- } else {
- send(size.getValue());
- }
- }
- @POST("/tshirt-size")
- boolean send(@Size.ValidSize int size) {
- return true;
- }
0 件のコメント:
コメントを投稿