いろいろガンガンいこうぜ。体も大事に

普段はJavaでAndroidアプリ開発しているプログラマーのブログです。

Hello Widget! (2) AppWidgetProviderについて

Widgetを作る際に作ったAppWidgetProviderのサブクラス。
最小実装のWidgetを作った時は,メソッドを何も定義しないクラスを作りました。


AppWidgetProviderクラス。
BroadcastReceiverのサブクラス。


中でどんな複雑なことをしているのだろう?
と思いきや,ソースコード自体は以外とシンプルでした。

void onReceive(Context context,Intent intent)の中で,
飛んで来たIntentのActionに対応したメソッドを呼び出しているだけだでした。

  • ACTION_APPWIDGET_UPDATE
  • ACTION_APPWIDGET_DELETED
  • ACTION_APPWIDGET_ENABLED
  • ACTION_APPWIDGET_DISABLED
  • ACTION_APPWIDGET_OPTIONS_CHANGED

というそれぞれのActionに対して,

  • void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
  • void onDeleted(Context context, int[] appWidgetIds)
  • void onEnabled(Context context)
  • void onDisabled(Context context)
  • void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)


というメソッドが,AppWidgetProviderのonReceiveメソッドの中で呼ばれていました。
appWidgetIdsなどの引数はIntentのExtrasから抽出していました。

ちなみに,AppWidgetProviderクラス自体のonUpdateメソッドなどは全く何もしていないメソッドでした。


アンドロイドデベロッパーのサイトによると,
そもそも,AppWidgetProviderクラスはアップウィジェットに関わるインテントのブロードキャストを扱うための便利クラスのようです。



それぞれのメソッドを自分なりにまとめてみました。


void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)

このメソッドは,アップウィジェットを更新するために呼ばれる。
間隔は,AppWidgetProviderInfoの中のupdatePeriodMillisで指定した間隔で呼ばれる。
また,アップウィジェットウィジェットを初期化した時にも呼ばれる。
そのためもし必要であれば,ビューを扱うイベントハンドラーを定義したりするべき。
ウィジェットの設定Activityを使う場合は,
アップウィジェットを初期化した時このメソッドが呼ばれないことに注意しないといけない。


void onDeleted(Context context, int[] appWidgetIds)

アップウィジェットがアップウィジェットホストから消されるごとに呼ばれる。


void onEnabled(Context context)

初めてアップウィジェットインスタンスが生成されたときに呼ばれる。
二つ同じ種類のアップウィジェットを置いたとしても一回しか呼ばれない。
データベースを初期化生成したリだとか,
全てのアップウィジェットインスタンスに対して一回やればいいという処理はここ。


・void onDisabled(Context context)

アップウィジェットの最後のインスタンスがアップウィジェットホストから消される時呼ばれる。
一時的なデータベースの削除など,onEnabledメソッドでやったことに対しての後処理はここでやるべきらしい。


・void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)

ウィジェットが初めて置かれた時と,サイズが変わったときに呼ばれる。
ウィジェットのサイズを取得できる。
(APIレベル16から)






ちなみに,

アップウィジェットを作成するために
AppWidgetProviderを継承せずに直接BroadCastReceiverを継承し,
それぞれのIntentのActionに応じた処理を記述して〜
という実装方法も可能らしいです。




それでは,また会えることを祈りつつ。

Gsonのユーザーガイドを見てみた (2)

前回に引き続き,Gsonのユーザーガイドを見ていきます。

配列

Gsonのユーザーガイドには,intとStringの配列の例がありました。

Person.java

package com.rmstar.gson.userguide;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return String.format("%s %d才", name, age);
    }
}

自分で定義したPersonクラスの配列も試してみました。



package com.rmstar.gson.userguide;

import com.google.gson.Gson;

public class ArrayExample {
    public static void main(String[] args) {
        Gson gson = new Gson();

        int[] ints = new int[] {
                1, 2, 3, 4, 5
        };
        String[] strings = new String[] {
                "abc", "def", "ghi"
        };
        Person[] persons = new Person[] {
                new Person("taro", 20),
                new Person("jiro", 17),
                new Person("saburo", 14),
        };

        System.out.println(gson.toJson(ints));
        System.out.println(gson.toJson(strings));
        System.out.println(gson.toJson(persons));

        String intsString = "[3,2,1,0]";
        String stringsString = "[\"AAA\",\"BBB\",\"CCC\"]";
        String personString = "[{\"name\":\"Alice\",\"age\":24},{\"name\":\"Bob\",\"age\":25}]";

        int[] deserializedInts = 
                gson.fromJson(intsString, int[].class);
        String[] deserializedStrings =
                gson.fromJson(stringsString, String[].class);
        Person[] deserializedPersons =
                gson.fromJson(personString, Person[].class);

        System.out.println("=== Deserialized Ints ===");
        for (int value : deserializedInts) {
            System.out.println(value);
        }

        System.out.println("=== Deserialized Strings ===");
        for (String value : deserializedStrings) {
            System.out.println(value);
        }

        System.out.println("=== Deserialized Persons ===");
        for (Person value : deserializedPersons) {
            System.out.println(value);
        }

    }
}


実行結果は,


[1,2,3,4,5]
["abc","def","ghi"]
[{"name":"taro","age":20},{"name":"jiro","age":17},{"name":"saburo","age":14}]
=== Deserialized Ints ===
3
2
1
0
=== Deserialized Strings ===
AAA
BBB
CCC
=== Deserialized Persons ===
Alice 24才
Bob 25才


となりました。



ついでに,多次元配列もやってみました。

package com.rmstar.gson.userguide;

import com.google.gson.Gson;

public class MultiArrayEamaple {
    public static void main(String[] args) {
        System.out.println("===Multidimensional Array===");
        int[][] multiArray = new int[][] {
                new int[3],
                new int[3],
                new int[3],
        };
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                multiArray[i][j] = i + 2 * j;
            }
        }
        for (int[] a : multiArray) {
            System.out.println(String.format("%d %d %d", a[0], a[1], a[2]));
        }

        System.out.println("===Serialize===");
        Gson gson = new Gson();
        String serialized = gson.toJson(multiArray);
        System.out.println(gson.toJson(multiArray));

        System.out.println("===Deserialize===");
        int[][] deserialzed = gson.fromJson(serialized, int[][].class);
        for (int[] a : deserialzed) {
            System.out.println(String.format("%d %d %d", a[0], a[1], a[2]));
        }
    }
}

実行結果は次の通りです。

===Multidimensional Array===
0 2 4
1 3 5
2 4 6
===Serialize===
[[0,2,4],[1,3,5],[2,4,6]]
===Deserialize===
0 2 4
1 3 5
2 4 6


多次元配列も可能なようですね。



コレクション

package com.rmstar.gson.userguide;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class CollectionExample1 {
    public static void main(String[] args) {
        Gson gson = new Gson();

        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);

        System.out.println("=== Serialize ===");
        System.out.println(gson.toJson(list));

        System.out.println("=== Deserialize ===");
        String jsonString = "[0,10,100]";
        Type listType = new TypeToken<List<Integer>>() {
        }.getType();

        List<Integer> deserialized = 
                gson.fromJson(jsonString, listType);
        System.out.println(deserialized);
        System.out.println(deserialized.getClass().getName());
    }
}


実行結果は,

=== Serialize ===
[1,2,3]
=== Deserialize ===
[0, 10, 100]
java.util.ArrayList

となりました。

SetやMapでもやってみようと思います。

package com.rmstar.gson.userguide;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class CollectionExample2 {
    public static void main(String[] args) {
        Gson gson = new Gson();

        Set<Integer> set = new TreeSet<Integer>();
        set.add(1);
        set.add(2);
        set.add(3);

        Map<String, Integer> map = new TreeMap<String, Integer>();
        map.put("a", 1);
        map.put("b", 2);

        System.out.println("=== Serialize ===");
        System.out.println(gson.toJson(set));
        System.out.println(gson.toJson(map));

        System.out.println("=== Deserialize ===");
        String setString = "[0,10,100]";
        String mapString = "{\"X\":10,\"Y\":-10}";
        Type setType = new TypeToken<Set<Integer>>() {
        }.getType();
        Type mapType = new TypeToken<Map<String, Integer>>() {
        }.getType();

        Set<Integer> deserializedSet =
                gson.fromJson(setString, setType);
        System.out.println(deserializedSet);
        System.out.println(deserializedSet.getClass().getName());

        Map<String, Integer> deserializedMap =
                gson.fromJson(mapString, mapType);
        System.out.println(deserializedMap);
        System.out.println(deserializedMap.getClass().getName());
    }
}


実行結果は,

=== Serialize ===
[1,2,3]
{"a":1,"b":2}
=== Deserialize ===
[0, 10, 100]
java.util.LinkedHashSet
{X=10, Y=-10}
java.util.LinkedHashMap


でした。

Type mapType = new TypeToken<Map<String, Integer>>() {
}.getType();

のように,TypeToken型のインスタンスを作る際の型引数に

  • Listインターフェース型
  • Setインターフェース型
  • Mapインターフェース型

を指定した場合,それぞれ

が生成されました。


当然インターフェースでなく,
インスタンス化したいクラスを指定してのインスタンス化も出来るようです。

package com.rmstar.gson.userguide;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.TreeMap;

public class CollectionExample3 {
    public static void main(String[] args) {
        Gson gson = new Gson();

        String listString = "[1,2,1]";
        String setString = "[0,10,100]";
        String mapString = "{\"X\":10,\"Y\":-10}";

        Type listType = new TypeToken<LinkedList<Integer>>() {
        }.getType();
        Type setType = new TypeToken<HashSet<Integer>>() {
        }.getType();
        Type mapType = new TypeToken<TreeMap<String, Integer>>() {
        }.getType();

        Class<?> listClass =
                gson.fromJson(listString, listType).getClass();
        Class<?> setClass =
                gson.fromJson(setString, setType).getClass();
        Class<?> mapClass =
                gson.fromJson(mapString, mapType).getClass();

        System.out.println(listClass.getName());
        System.out.println(setClass.getName());
        System.out.println(mapClass.getName());
    }
}

実行結果は,

java.util.LinkedList
java.util.HashSet
java.util.TreeMap

です。





総称型のシリアライズとデシリアライズ

toJson(obj)メソッドを呼んだ時,
内部的にobj.getClass()メソッドを呼んでいるそうです。
シリアライズするフィールドの情報を得るためだそうです。
同じように,fromJsonメソッドの第二引数にもclass型を入れますよね。

非総称型のオブジェクトをデシリアライズする場合は
int.classのようなクラス型でうまくいくのですが,
総称型のオブジェクトの場合うまくいきません。
総称型の情報が無くなっているからというのが原因みたいです。

Fuga.java

package com.rmstar.gson.userguide;

public class Fuga {
    int fuga;

    @Override
    public String toString() {
        return String.valueOf(fuga);
    }
}

Hoge.java

package com.rmstar.gson.userguide;

public class Hoge<T> {
    T hoge;

    @Override
    public String toString() {
        return String.format("Hoge : %s", hoge.toString());
    }
}

GenericTypeExample.java

package com.rmstar.gson.userguide;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;

public class GenericTypeExample {
    public static void main(String[] args) {

        System.out.println("=== Define ===");
        Gson gson = new Gson();

        Hoge<Fuga> a = new Hoge<Fuga>();
        a.hoge = new Fuga();
        a.hoge.fuga = 1;
        System.out.println(a);

        System.out.println("=== Serealize ===");
        String jsonA = gson.toJson(a);
        System.out.println(jsonA);

        System.out.println("=== Deserealize ===");
        Hoge<Fuga> deserializedA;

        deserializedA = gson.fromJson(jsonA, Hoge.class);
        System.out.println(deserializedA);

        Type type = new TypeToken<Hoge<Fuga>>() {
        }.getType();
        deserializedA = gson.fromJson(jsonA, type);
        System.out.println(deserializedA);
    }
}


GenericTypeExample.javaの実行結果は,

=== Define ===
Hoge : 1
=== Serealize ===
{"hoge":{"fuga":1}}
=== Deserealize ===
Hoge : {fuga=1.0}
Hoge : 1

でした。

Hoge.classをfomJsonの引数に取った場合には,
正しくデシリアライズできていないみたいです。




今回はこの辺で。

それでは,また会えることを祈りつつ。

Gsonのユーザーガイドを見てみた (1)

Gson User Guideに載っているサンプルを実際にやってみたり,書いてあるポイントを自分なりにまとめてみました。



基本型

package com.rmstar.gson.userguide;

import com.google.gson.Gson;

public class Primitives {
    public static void main(String[] args) {
        serialize();
        System.out.println();
        deserialize();
    }

    public static void serialize() {
        Gson gson = new Gson();

        System.out.println("===Serialize===");
        System.out.println(gson.toJson(1));
        System.out.println(gson.toJson("abcd"));
        System.out.println(gson.toJson(new Long(10)));
        int[] values = {
                1
        };
        System.out.println(gson.toJson(values));
    }

    public static void deserialize() {
        Gson gson = new Gson();

        int oneInt = gson.fromJson("1", int.class);
        Integer oneInteger = gson.fromJson("1", Integer.class);
        Boolean falseBoolean = gson.fromJson("false", Boolean.class);
        String str = gson.fromJson("\"abc\"", String.class);
        String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);

        System.out.println("===Deserialize===");
        System.out.println(oneInt);
        System.out.println(oneInteger);
        System.out.println(falseBoolean);
        System.out.println(str);
        System.out.println(anotherStr[0]);
        System.out.println(String.format("length : %d", anotherStr.length));
    }
}


実行結果は,


===Serialize===
1
"abcd"
10
[1]

===Deserialize===
1
1
false
abc
abc
length : 1

となりました。





オブジェクトの例

package com.rmstar.gson.userguide;

import com.google.gson.Gson;

public class ObjectExamples {
    private static class SampleObject {
        private int value1 = 1;
        private String value2 = "abc";
        private transient int value3 = 3;

        public SampleObject() {
        }
    }

    public static void main(String[] args) {
        SampleObject sampleObject = new SampleObject();
        Gson gson = new Gson();
        String jsonString = gson.toJson(sampleObject);
        System.out.println(jsonString);

        SampleObject deserialized = gson.fromJson(jsonString, SampleObject.class);
    }
}

実行結果は,

{"value1":1,"value2":"abc"}


でした。

ポイントは

  • フィールドに基本型を使った場合は問題なし。
  • JSON形式への変換,JSON形式からの変換。どちらもフィールドにアノテーションをつける必要なし。そのクラスと親クラスの全てのフィールドが変換に含まれる。
  • transient修飾子がついた場合,JSON形式への変換,JSON形式からの変換どちらも無視される。
  • nullを正しく扱うことが出来る。
    • JSON形式の文字列への変換(シリアライゼーション)では,値がnullのフィールドは変換結果のJSON文字列中に含まれない。
    • JSON形式の文字列からの変換(デシリアライゼーション)では,文字列中に含まれていない要素に対応するフィールドはnullとなる。
  • フィールドがsynethticならば,シリアライゼーションでも,デシリアライゼーションでも無視される。
  • インナークラス中のアウタークラス,無名クラス,ローカルクラスに該当するフィールドは無視されて,シリアライゼーションにもデシリアライゼーションにも含まれない。


だそうです。




いろいろ試してみました。

package com.rmstar.gson.userguide;

import com.google.gson.Gson;

public class Ex1 {

    private static class A {
        private int a;

        A(int a) {
            this.a = a;
        }
    }

    private static class B extends A {
        private int b;

        B(int a, int b) {
            super(a);
            this.b = b;
        }
    }

    private static class C extends B {
        private int c;

        C(int a, int b, int c) {
            super(a, b);
            this.c = c;
        }
    }

    public static void main(String[] args) {
        C c = new C(1, 2, 3);
        Gson gson = new Gson();
        System.out.println(gson.toJson(c));
    }
}


実行結果は,

{"c":3,"b":2,"a":1}


親クラスのプライベートなフィールドもJSON形式に変換されるらしいです。



次にnullの取り扱い。

package com.rmstar.gson.userguide;

import com.google.gson.Gson;

public class Ex2 {

    private static class Sample {
        private String name;
        private String value;

        Sample(String name, String value) {
            this.name = name;
            this.value = value;
        }
    }

    public static void main(String[] args) {
        Gson gson = new Gson();

        Sample sampleA = new Sample("a", "v1");
        Sample sampleB = new Sample("b", null);

        System.out.println(gson.toJson(sampleA));
        System.out.println(gson.toJson(sampleB));

        String sampleCJsonString = "{\"name\":\"c\"}";
        Sample sampleC = gson.fromJson(sampleCJsonString, Sample.class);
        System.out.println(sampleC.name);
        System.out.println(sampleC.value);
    }
}

実行結果は,

{"name":"a","value":"v1"}
{"name":"b"}
c
null

となるようです。




ネストされたクラス(インナークラスを含む)

Gsonはとても簡単にstaticなネストされたクラスをシリアライズできるようです。
また,Gsonはstaticなネストされたクラスをデシリアライズすることも可能です。


しかし,Gsonは純粋なインナークラスは自動的にデシリアライズできません。
インナークラスの引数なしのコンストラクターは,そのインナークラスを保持しているオブジェクトの参照が必要で,それがデシリアライズの時に利用不可能だからだそうです。


インナークラスをstaticにするか,カスタマイズされたInstanceCreatorを作る必要があるようです。


InstanceCreatorを使ってみました。
A.java

package com.rmstar.gson.userguide;

public class A {
    public String a;

    class B {

        public String b;

        public B() {
            // No args constructor for B
        }
    }
}


InstanceCreatorForB.java

package com.rmstar.gson.userguide;

import com.google.gson.InstanceCreator;

import java.lang.reflect.Type;

public class InstanceCreatorForB implements InstanceCreator<A.B> {
    private final A a;

    public InstanceCreatorForB(A a) {
        this.a = a;
    }

    public A.B createInstance(Type type) {
        return a.new B();
    }
}


Ex3.java

package com.rmstar.gson.userguide;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Ex3 {

    public static void main(String[] args) {
        A instanceA = new A();

        InstanceCreatorForB creator =
                new InstanceCreatorForB(instanceA);

        Gson gson = new GsonBuilder()
                .registerTypeAdapter(A.B.class, creator)
                .create();

        A.B instanceB = gson.fromJson("{\"b\":\"abc\"}", A.B.class);
        System.out.println(instanceB.b);
    }
}


ちょっと面倒ですね。





今回はこの変で。


それでは,また会えることを祈りつつ。

Gsonを使ってみた。(1)

Gsonを使ってみた。



Gson
JavaのオブジェクトをJSON形式に変換することができるライブラリ。
JSON文字列をJavaのオブジェクトに変換することもできるようです。


JavaオブジェクトをJSONに変換するオープンソースプロジェクトはいくつか存在するのだけれど次のような問題がもあるものも多いらしいです。


Gsonの目標は,

  • JavaオブジェクトからJSONへの変換とその逆の変換をするシンプルなtoJson()・fromJson()メソッドの提供
  • 元々ある修正不可能なオブジェクトのJSONへのそしてJSONからの変換を可能にする
  • Javaジェネリクのさらなるサポート
  • オブジェクトの特別な表現を可能にする
  • 任意の複雑なオブジェクトのサポート(深い継承階層とジェネリックの多用など)

らしい。




つかってみました。


こんな感じのシンプルなモデルを
Person.java

public class Person {
    private final String mName;
    private final int mAge;

    public Person(String name, int age) {
        super();
        mName = name;
        mAge = age;
    }

    public String getName() {
        return mName;
    }

    public int getAge() {
        return mAge;
    }
}


JSON形式にしてみました。



import com.google.gson.Gson;

public class Main {

    public static void main(String[] args) {
        Person taro = new Person("太郎", 20);

        Gson gson = new Gson();

        String jsonString = gson.toJson(taro);

        System.out.println(jsonString);
    }
}


実行結果はこのような感じになりました。



{"mName":"太郎","mAge":20}


複雑なモデルの変換とかはまたいつか。



それでは,また会えることを祈りつつ。

Javaの列挙型について(3)

前回,前々回に引き続き列挙型。


列挙型に組み込まれているいくつかのメソッドを確認。


Month.java

package com.rmstar.enumsample;

public enum Month {
    JANUARY("睦月", "冬"),
    FEBRUARY("如月", "冬"),
    MARCH("弥生", "春"),
    APRIL("卯月", "春"),
    MAY("皐月", "春"),
    JUNE("水無月", "夏"),
    JULY("文月", "夏"),
    AUGUST("葉月", "夏"),
    SEPTEMBER("長月", "秋"),
    OCTOBER("神無月", "秋"),
    NOVEMBER("霜月", "秋"),
    DECEMBER("師走", "冬"), ;

    private final String mOldJapaneseName;
    private final String mSeason;

    private Month(String oldJapaneseName, String season) {
        mOldJapaneseName = oldJapaneseName;
        mSeason = season;
    }

    public String getOldJapaneseName() {
        return mOldJapaneseName;
    }

    public String getSeason() {
        return mSeason;
    }
}

前回に引き続き月を表す列挙型を使います。





すべてのインスタンスを列挙する

    private static void showAll() {
        for (Month month : Month.values()) {
            System.out.println(month.getOldJapaneseName());
        }
    }

いちいちべた書きで,全要素を書く必要はありません。
staticメソッドMonth.values()で,全要素が入った配列を取得出来ます。



文字列と列挙型

列挙型で定義したインスタンスが使えるメソッド。
String name();

Object型で定義されているメソッド
String toString();

System.out.println(Month.JULY.toString());
System.out.println(Month.JULY.name());

実行結果は

JULY
JULY

となっています。

同じですね。
ただし,String name()は,finalがついていてオーバーライド禁止になっています。
String name()の返り値は常に列挙型を定義した際に
列挙した文字列(Monthだったら,JANUARYやDECEMBERなど)になります。

一方で,toString()はオーバーライド可能です。
より読みやすい文字列を表示出来るように自分で再定義することも可能です。
また,ライブラリ中の列挙型の場合,
name()より更に読みやすい名前が帰ってくることもあると思いますので,
デバックで用いるのならば,name()よりtoString()の方を選択する方がいいと思います。

static メソッド valueOf(String)
列挙型を定義する際に列挙した文字列を引数とした場合,
その列挙型のインスタンスを返します。
ちなみに列挙されていない文字列な場合,IllegalArgumentExceptionが投げられます。



compareTo()とordinal()

列挙型は,
Enumは Comparableインターフェースを実装しているので,
compareToメソッドが使えます。

System.out.println(Month.APRIL.compareTo(Month.JULY));
System.out.println(Month.JULY.compareTo(Month.JULY));
System.out.println(Month.JULY.compareTo(Month.APRIL));

実行結果は,以下の通り。

-3
0
3

列挙型を定義する際に,先に定義した要素が先に小さい・先に来るようになっています。


そのため次のように,TreeSetを使えばソートもしてくれます。

Set<Month> monthSet = new TreeSet<Month>();
monthSet.add(Month.DECEMBER);
monthSet.add(Month.JANUARY);
monthSet.add(Month.JULY);
System.out.println(monthSet);

実行結果は,

[JANUARY, JULY, DECEMBER]

となります。


int ordinal() メソッド

System.out.println(Month.JANUARY.ordinal());
System.out.println(Month.JULY.ordinal());
System.out.println(Month.DECEMBER.ordinal());

実行結果は,

0
6
11

となります。

その要素の序数を取得出来ます。
これとEnum.values()を用いて次の要素を取得するメソッドを書きました。

private static Month getNext(Month month) {
    Month[] values = Month.values();
    int index = (month.ordinal() + 1) % values.length;
    return values[index];
}

public static void main(String[] args) {
    System.out.println(getNext(Month.JANUARY));
    System.out.println(getNext(Month.JULY));
    System.out.println(getNext(Month.DECEMBER));
}


実行結果は,

FEBRUARY
AUGUST
JANUARY

となります。


それでは,また会えることを祈りつつ。

Javaの列挙型について(2)

前回に引き続き列挙型。



今回は月を表す列挙型と,その使用例。




Month.java

package com.rmstar.enumsample;

public enum Month {
    JANUARY,
    FEBRUARY,
    MARCH,
    APRIL,
    MAY,
    JUNE,
    JULY,
    AUGUST,
    SEPTEMBER,
    OCTOBER,
    NOVEMBER,
    DECEMBER, ;
}


Main.java

package com.rmstar.enumsample;

public class Main {

    private static void showInOldJapanese(Month month) {
        switch (month) {
            case JANUARY:
                System.out.println("睦月");
                break;
            case FEBRUARY:
                System.out.println("如月");
                break;
            case MARCH:
                System.out.println("弥生");
                break;
            case APRIL:
                System.out.println("卯月");
                break;
            case MAY:
                System.out.println("皐月");
                break;
            case JUNE:
                System.out.println("水無月");
                break;
            case JULY:
                System.out.println("文月");
                break;
            case AUGUST:
                System.out.println("葉月");
                break;
            case SEPTEMBER:
                System.out.println("長月");
                break;
            case OCTOBER:
                System.out.println("神無月");
                break;
            case NOVEMBER:
                System.out.println("霜月");
                break;
            case DECEMBER:
                System.out.println("師走");
                break;
        }
    }

    private static void showSeason(Month month) {
        switch (month) {
            case MARCH:
            case APRIL:
            case MAY:
                System.out.println("春");
                break;
            case JUNE:
            case JULY:
            case AUGUST:
                System.out.println("夏");
                break;
            case SEPTEMBER:
            case OCTOBER:
            case NOVEMBER:
                System.out.println("秋");
                break;
            case DECEMBER:
            case JANUARY:
            case FEBRUARY:
                System.out.println("冬");
                break;
        }
    }

    public static void main(String[] args) {
        showInOldJapanese(Month.JULY);
        showSeason(Month.JULY);
    }
}



長い!
switch文...
これにもしかしたら,
1月と表示するメソッドとかDECとか表示するメソッドが加わるかもしれない。
良くない。





Javaの列挙型は,CやC#と違いクラス。



文月や夏といったものはJULYに紐づくものだから,
JULYインスタンスのフィールドに持たせて,ゲッタでアクセスするよう変更した。
変更後は以下の通り。




Month.java(変更後)

package com.rmstar.enumsample;

public enum Month {
    JANUARY("睦月", "冬"),
    FEBRUARY("如月", "冬"),
    MARCH("弥生", "春"),
    APRIL("卯月", "春"),
    MAY("皐月", "春"),
    JUNE("水無月", "夏"),
    JULY("文月", "夏"),
    AUGUST("葉月", "夏"),
    SEPTEMBER("長月", "秋"),
    OCTOBER("神無月", "秋"),
    NOVEMBER("霜月", "秋"),
    DECEMBER("師走", "冬"), ;

    private final String mOldJapaneseName;
    private final String mSeason;

    private Month(String oldJapaneseName, String season) {
        mOldJapaneseName = oldJapaneseName;
        mSeason = season;
    }

    public String getOldJapaneseName() {
        return mOldJapaneseName;
    }

    public String getSeason() {
        return mSeason;
    }
}


Main.java(変更後)

package com.rmstar.enumsample;

public class Main {
    public static void main(String[] args) {
        System.out.println(Month.JULY.getOldJapaneseName());
        System.out.println(Month.JULY.getSeason());
    }
}

すっきり。
もう一つ。


MonthのInstance全ての旧暦を表示したい。

        System.out.println(Month.JANUARY.getOldJapaneseName());
        System.out.println(Month.FEBRUARY.getOldJapaneseName());
        ・・・
        System.out.println(Month.DECEMBER.getOldJapaneseName());

という風に,書く?
めんどくさい。




こういうことを楽にする仕組みが列挙型にはあるらしい。
次回は,そのような列挙型でできることを確認したいと思います。






では,また会えることを祈りつつ。

Javaの列挙型について(1)

Javaの列挙型について。


    public static final int DIRECTION_NORTH = 0;
    public static final int DIRECTION_EAST = 1;
    public static final int DIRECTION_SOUTH = 2;
    public static final int DIRECTION_WEST = 3;

    private static void showDirection(int direction) {
        switch (direction) {
            case DIRECTION_NORTH:
                System.out.println("北");
                break;
            case DIRECTION_EAST:
                System.out.println("東");
                break;
            case DIRECTION_WEST:
                System.out.println("西");
                break;
            case DIRECTION_SOUTH:
                System.out.println("南");
                break;
            default:
                break;
        }
    }

    public static void main(String[] args) {
        showDirection(DIRECTION_NORTH);
    }


方角として定数を定義。
その方角を引数して取る(ことを想定した)メソッドと,
それをつかったmain関数。


しかし,こんなコードも書けてしまう。

    public static void main(String[] args) {
        showDirection(0);
        showDirection(-1);
    }


showDirectionメソッドは引数として方角を取ることを想定している。
しかし,実際はint型の引数だから,方角の定数以外も引数にできてしまう。
そのため,北の入力としてDIRECTION_NORTHでなく0を入れることもできる。
また,0,1,2,3,以外の値も入力にできる。



良くない。



実際には上のようなソースコードを書くことは無いと思うけれど。
結果的にそうなってしまうパターンもあるのでは。(あるかな?)




そこで,列挙型を使ってみた。

Direction.java

package com.rmstar.enumsample;

public enum Direction {
    NORTH,
    EAST,
    SOUTH,
    WEST,
}


Main.java

package com.rmstar.enumsample;

public class Main {

    private static void showDirection(Direction direction) {
        switch (direction) {
            case NORTH:
                System.out.println("北");
                break;
            case EAST:
                System.out.println("東");
                break;
            case WEST:
                System.out.println("西");
                break;
            case SOUTH:
                System.out.println("南");
                break;
            default:
                break;
        }
    }

    public static void main(String[] args) {
        showDirection(Direction.NORTH);
    }
}


列挙体を使えば,showDirectionの中に0や-1みたいな想定外の入力が入ることも無い。




Javaの 列挙型は他の言語と違ってクラス。
次はこの当りを確認したいと思います。





それでは,また会えることを祈りつつ。