2013年8月23日金曜日

[Java]google gsonのはまりポイント

JSONをアノテーションでPOJOにバインディングするgsonは、JSONObjectでパースする原始的な処理?より、はるかに楽ができます。 最初使ったときは感動しましたね。こういうライブライがつかえないプロジェクトでは、同様の処理を実現するためにリフレクションであれして。。。なんていう残念な結末が目に浮かびます。

しかし、そのgsonにも、これはないんじゃない?的な結果になることがいくつかあります。
そのいくつかはこんな感じ。
  1. <T> T Gson#fromJson(String json, Type typeOfT) で第一引数にnull渡すと結果がnull
  2. <T> T Gson#fromJson(String json, Type typeOfT) で第一引数にから文字渡すと結果がnull
  3. <T> T Gson#fromJson(Reader json, Class<T> classOfT) で第一引数がのReaderが即EOFになると結果がnull
そうなんです。解析できない系のインプットが入ると、結果がnullになっちゃうんです。
1は、妥当だと思いますが、2,3は例外はくかデフォルトで生成したオブジェクト返せよ!って感じがします。
ということで結果のnullチェックが必要になるという残念な感じになっています。
これ気に入らないなら、Null Object Supportを使えよって話ですね。しかしこれではインプットがnullだからそうなっているのか、空文字系("")だからそうなるのかわかりません。
個人的な考えでは、前者はバグで後者はデータ不正ですね。


ここまでは気を付けるよの話なんですが、問題は

  • JSON構文エラーの際に発生する構文エラーがRuntime系の例外

です。そうなんです。コンパイラーでのチェックができないという残念な結果になります。
・・・外部からインプットするJSONの構文チェックは当然すべきであり、これがランタイムになるというのはちょっと理解できないです。
みなさん、こういうのってどうしてるんでしょうか。

2013年7月25日木曜日

[Java]数値のラッパー型をintに変換する

引数で何らかの数値ラッパー型がObjectで引き渡される。それをintとして利用したい的なことありませんか? ありませんね。
でも、汎用的な変換ライブラリなどではそんなことあるかもしれませんね。 instanceofを全部の数値ラッパー型に適応して。。。涙が出そうになります。ということで調べてみるとNumberというインタフェースがあり、これが使えることがわかりました。 IntegerやDoubleのリファレンスを見るとわかるとおり、ラッパー型のクラスはみんなNumberを継承しているんです。 コードはこんな感じ。

public static void main(String[] args) {
	Object d = Double.valueOf(3.5d);
	System.out.println("3.5d=" + toInt(d));
 
	Object f = Float.valueOf(2.5f);
	System.out.println("2.5f=" + toInt(f));
 
	Object l = Long.valueOf(20l);
	System.out.println("20l=" + toInt(l));
 
	Object b = Byte.valueOf((byte) 20);
	System.out.println("20b=" + toInt(b));
 
	Object s = Short.valueOf((short) 20);
	System.out.println("20s=" + toInt(s));
 
	Object bd = new BigDecimal(4.5);
	System.out.println("4.5bd=" + toInt(bd));
 
	Object al = new AtomicLong(21);
	System.out.println("21al=" + toInt(al));
 }
 
/**
 * int変換.
 * 
 * 数値ラッパーをintに変換する。
 * 
 * @param obj
 * @return 変換した数値
 */
public static int toInt(Object obj) {
	if (!(obj instanceof Number)) {
		throw new IllegalArgumentException("not number!!");
	}
	return ((Number) obj).intValue();
}
実行結果はこんな感じ。

3.5d=3
2.5f=2
20l=20
20b=20
20s=20
4.5bd=4
21al=21

2013年7月23日火曜日

[Java]JSONをPOJOにバインドするとき、オリジナルの変換を行う

JSONからPOJO(Bean)に変換する処理はめんどくさいです。たとえば、
  • JSONは文字列で、Java側はEnum
このケースでは、文字列=Enumの定義名という式が成り立てば、自動で変換されます。下のstep1がそれに該当します。しかし文字列がenumと一致しない場合、正しく変換されません。おそらく初期値になってしまうでしょう。以下の例ではnullになってしまっています(enumはnullを許容するんです)。
次に、
  • JSON上は数値なんだけど、Java上はEnum
この場合、自動的な変換では明らかに無理です。こういう場合、JsonDeserializer<T>を使います。 以下の例では、数値をenumにしています。こちらも存在しない値の場合、nullを返却しています。 実装した例はこんな感じ。

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

        // タイプを取り出す(List)
        Type type = new TypeToken<List<Signal>>() {
        }.getType();

        // JSON->List<Signal>
        List<Signal> signal = gson.fromJson(new InputStreamReader(
                GsonMap.class.getResourceAsStream("signal2.json")), type);

        System.out.println("---step1---");
        System.out.println(signal);

        // JSON->List<Signal>
        // #1 TypeAdapterを設定したGsonを生成する
        Gson gson2 = new GsonBuilder().registerTypeAdapter(SignalStat.class,
                new SignalStatDeserializer()).create();
        List<Signal> signal2 = gson2.fromJson(new InputStreamReader(
                GsonMap.class.getResourceAsStream("signal3.json")), type);

        System.out.println("---step2---");
        System.out.println(signal2);
    }

    /**
     * 解析結果を受け取るクラス
     */
    static class Signal {
        private SignalStat stat;
        private String message;
        private SignalStat next;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            sb.append("stat=").append(stat);
            sb.append(" message=").append(message);
            sb.append(" next=").append(next);
            sb.append("]");

            return new String(sb);
        }
    }

    /**
     * ステータスの表現.
     */
    enum SignalStat {
        Red, Yellow, Blue;

        // #2 変換用のMAP(int -> SignalStat)
        static Map<Integer, SignalStat> CONVERTER = new HashMap<Integer, SignalStat>();

        static {
            for (SignalStat stat : SignalStat.values()) {
                CONVERTER.put(stat.ordinal(), stat);
            }
        }
    }

    /**
     * SignalStat用Deserializer.
     */
    static private class SignalStatDeserializer implements
            JsonDeserializer<SignalStat> {

        @Override
        public SignalStat deserialize(JsonElement json, Type typeOfT,
                                      JsonDeserializationContext context) throws JsonParseException {
            // #3 int -> SignalStat
            return SignalStat.CONVERTER.get(json.getAsInt());
        }
    }
}
ポイントは以下の通り
  • #1:TypeAdapterを設定したGsonを生成するためにGsonBuilderをnewしregisterTypeAdapterでDeserializerを指定する
  • #2:enumの中に、変換用Mapを用意しておく。staticでOKです
  • #3:JsonDeserializerを実装する。#2の変換用Mapを使ってint->enumを実現している
入力データはこんな感じ(signal2.json)

[
    {
        "stat": "Red",
        "message": "とまれ",
        "next": "Blue"
    },
    {
        "stat": "Yellow",
        "message": "とまっとけ",
        "next": "Red"
    },
    {
        "stat": "Invalid",
        "message": "すすんでもいいよ",
        "next": "Yellow"
    }
]
入力ry (signal3.json)

[
    {
        "stat": 0,
        "message": "とまれ",
        "next": 2
    },
    {
        "stat": 1,
        "message": "とまっとけ",
        "next": 0
    },
    {
        "stat": 100,
        "message": "すすんでもいいよ",
        "next": 1
    }
]
出力結果はこんな感じ
step1ではInvalidは存在しないのでnullとなり、step2では100は存在しないのでnullになっています。

---step1---
[[stat=Red message=とまれ next=Blue], [stat=Yellow message=とまっとけ next=Red], [stat=null message=すすんでもいいよ next=Yellow]]
---step2---
[[stat=Red message=とまれ next=Blue], [stat=Yellow message=とまっとけ next=Red], [stat=null message=すすんでもいいよ next=Yellow]]

2013年7月22日月曜日

[Android]透明なアクティビティ

テーマ設定で透明なアクティビティが作れます。えぇまぁ AndroidManifest.xmlに書くだけです。

<activity android:name="com.hoge.TransparentActivity"
 android:theme="@android:style/Theme.Translucent"
 android:label="Transparent"/>

2013年7月21日日曜日

[Java]JSONをJsonObject/JsonArrayにバインドする

JSONからPOJO(Bean)に変換するんだけど、一部分だけJsonObjectに落としたい。そんな時はありませんか?
どんなデータが入ってくるのか決まっていない。けどキーは同じみたいな。結果的に汎用型で受け付けたい。そんなケースです。
いろいろ調べたんだけど方法があわからず。でしたが、実は普通にJsonObjectやJsonArrayつかえば良い。ということがわかりました。 配列の場合だけ気を付けないとダメ。という残念なことになっています。
コードはこんな感じ。


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

        // JSON->JsonObject(Map)
        JsonObject his = gson.fromJson(new InputStreamReader(GsonList.class
                .getResourceAsStream("history_map.json")), JsonObject.class);

        System.out.println("---step1 Map---");
        System.out.println(his);

        // JSON->JsonArray(List)JsonArrayじゃないとキャストに失敗する。。。
        JsonArray his2 = gson.fromJson(new InputStreamReader(GsonList.class
                .getResourceAsStream("history_list.json")), JsonArray.class);

        System.out.println("---step2 List---");
        System.out.println(his2);

        // JSON->JsonObject(POJO)
        JsonObject his3 = gson.fromJson(new InputStreamReader(GsonList.class
                .getResourceAsStream("history.json")), JsonObject.class);
        System.out.println("---step3 Object---");
        System.out.println(his3);
    }

    /**
     * 解析結果を受け取るクラス
     */
    static class History {
        private int key;
        private String message;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            sb.append("key=").append(key);
            sb.append(" message=").append(message);
            sb.append("]");

            return new String(sb);
        }
    }
}
出力結果はこんな感じ。

---step1 Map---
{"kentousi":{"key":894,"message":"遣唐使"},"kamakura1":{"key":1192,"message":"鎌倉幕府"},"kamakura2":{"key":1185,"message":"鎌倉幕府"}}
---step2 List---
[{"key":894,"message":"遣唐使"},{"key":1192,"message":"鎌倉幕府"},{"key":1185,"message":"鎌倉幕府"}]
---step3 Object---
{"key":894,"message":"遣唐使"}
インプットしたデータは省略。

[Java]JSONをPOJOにバインドする(List,[])

JSONからPOJO(Bean)に変換する処理はめんどくさいです。 Google社製のGSONでは、データ形式にListや配列としてバインドすることができます。 で、コードはこんな感じ。

class GsonList {

    public static void main(String[] args) {
        Gson gson = new Gson();
        // List.classと書けないので、TypeTokenを使ってTypeを取り出す
        Type type = new TypeToken>() {}.getType();

        // JSON->List
        List his = gson.fromJson(new InputStreamReader(
                GsonList.class.getResourceAsStream("history_list.json")), type);

        System.out.println("---step1 List---");
        System.out.println(his);

        // JSON->POJO[]
        History[] his2 = gson.fromJson(new InputStreamReader(
                GsonList.class.getResourceAsStream("history_list.json")), History[].class);

        System.out.println("---step2 []---");
        System.out.println(Arrays.toString(his2));
    }

    /**
     * 解析結果を受け取るクラス
     */
    static class History {
        private int key;
        private String message;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            sb.append("key=").append(key);
            sb.append(" message=").append(message);
            sb.append("]");

            return new String(sb);
        }
    }
}
出力結果は次の通り。

---step1 List---
[[key=894 message=遣唐使], [key=1192 message=鎌倉幕府], [key=1185 message=鎌倉幕府]]
---step2 []---
[[key=894 message=遣唐使], [key=1192 message=鎌倉幕府], [key=1185 message=鎌倉幕府]]
インプットしたJSONは次の通り。

[
    {
        "key": 894,
        "message": "遣唐使"
    },
    {
        "key": 1192,
        "message": "鎌倉幕府"
    },
    {
        "key": 1185,
        "message": "鎌倉幕府"
    }
]

[Java]JSONをPOJOにバインドする(Map)

JSONからPOJO(Bean)に変換する処理はめんどくさいです。
Google社製のGSONでは、データ形式にMapとしてバインドすることができます。 残念なことに、Map形式では順序を維持できないという課題があります。
しかしこれはJSONの仕様であり、GSONとしてはその仕様に準じているだけ。といえますね。 

コードはこんな感じ。

public class GsonMap {

    public static void main(String[] args) {
        Gson gson = new Gson();
        // Map.classと書けないので、TypeTokenを使ってTypeを取り出す
        Type type = new TypeToken>() {
        }.getType();

        // JSON->Map
        Map his = gson.fromJson(new InputStreamReader(
                GsonMap.class.getResourceAsStream("history_map.json")), type);

        System.out.println(his);
    }

    /**
     * 解析結果を受け取るクラス
     */
    static class History {
        private int key;
        private String message;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            sb.append("key=").append(key);
            sb.append(" message=").append(message);
            sb.append("]");

            return new String(sb);
        }
    }
}
実行結果はこんな感じ

{
    "kentousi"=["key"=894 "message"="遣唐使"],
    "kamakura1"=["key"=1192 "message"="鎌倉幕府"],
    "kamakura2"=["key"=1185 "message"="鎌倉幕府"]
}
インプットに使ったJSONはこんな感じ

{
    "kentousi": {
        "key": 894,
        "message": "遣唐使"
    },
    "kamakura1": {
        "key": 1192,
        "message": "鎌倉幕府"
    },
    "kamakura2": {
        "key": 1185,
        "message": "鎌倉幕府"
    }
}

[Java]JSONをPOJOにバインドする

JSONからPOJO(Bean)に変換する処理はめんどくさいです。リフレクションでルールを決めて変換。というのもめんどくさいですね。 そこで、JSONを解析するライブラリを利用します。 JSON系のライブラリはいくつかありますが、今回はGoogle社製のGSONを使ってみました。コードはこんな感じ。 よく紹介されているレベルのコードと変わりませんが、以下のようなポイントがあります。
  • private  static final の変数に値を突っ込んでいる
  • 内部クラス(staticですが)を利用する
  • 変数名と異なる項目をバインドしている
この辺を気にする人には参考になると思います。

public class GsonBasic {

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

        //JSON->POJO(1)
        History his = gson.fromJson(
                new InputStreamReader(GsonBasic.class
                        .getResourceAsStream("history.json")), History.class);
        System.out.println("---step1---");
        System.out.println(his);

        //JSON->POJO(1)
        History2 his2 = gson.fromJson(
                new InputStreamReader(GsonBasic.class
                        .getResourceAsStream("history.json")), History2.class);
        System.out.println("---step2---");
        System.out.println(his2);
    }

    // 内部クラスにする場合、staticにしないと面倒が起る

    /**
     * 解析結果を受け取るクラス(1)
     */
    static class History {
        // final にすることもできる
        private final int key;
        private final String message;

        // コンストラクタで初期化することで、GSONでもセットできる
        public History() {
            key = 0;
            message = null;
        }

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("key=").append(key);
            sb.append(" message=").append(message);

            return new String(sb);
        }
    }

    /**
     * 解析結果を受け取るクラス(2)
     */
    static class History2 {
        // JSONのキー名と変数名が異なる場合、@SerializedNameを使う
        @SerializedName("key")
        private int mKey = 0;
        @SerializedName("message")
        private String mMessage = "";

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("key=").append(mKey);
            sb.append(" message=").append(mMessage);

            return new String(sb);
        }
    }
}
出力結果は次の通り。

---step1---
key=894 message=遣唐使
---step2---
key=894 message=遣唐使
インプットしたJSONは次の通り。

{
    "key": 894,
    "message": "遣唐使"
}

2013年7月6日土曜日

[.NET]Dictionaryを素直?にJSONで出力する

.NET系でJSONを出力する場合、DataContractJsonSerializerというクラスがあり、これを使えばそれっぽく出力されます。
しかし、Dictionaryで持っている値を出力すると、なんかkeyとvalueの配列という残念な形式にシリアライズされてしまいます。
他にいい方法ないかなぁと思って調査してみたところ、.NET4.0以降では、もう一種類JSONのシリアライザがあるのがわかり、
試したところ、期待するような形式で出てきました。ということでコードはこんな感じ。


private void button1_Click(object sender, EventArgs e)
{
    // using System.Web.Script.Serialization;
    var json = new JsonData();
    json.item_key = "歴史";

    json.label_position["kentousi"] = new { key = 894, Message = "遣唐使" };
    json.label_position["kamakura1"] = new { key = 1192, Message = "鎌倉幕府" };
    json.label_position["kamakura2"] = new { key = 1185, Message = "鎌倉幕府" };
    json.label_position["gomi"] = new { key = "string", Message = new string[]{"array1","array2"} };

    var sel = new JavaScriptSerializer();
    System.Diagnostics.Debug.WriteLine(sel.Serialize(json));

}

class JsonData
{
    public string item_key;
    public Dictionary<string, Object> label_position = new Dictionary<string, Object>();
}
出力結果はこんな感じ。

{
    "item_key": "歴史",
    "label_position": {
        "kentousi": {
            "key": 894,
            "Message": "遣唐使"
        },
        "kamakura1": {
            "key": 1192,
            "Message": "鎌倉幕府"
        },
        "kamakura2": {
            "key": 1185,
            "Message": "鎌倉幕府"
        },
        "gomi": {
            "key": "string",
            "Message": [
                "array1",
                "array2"
            ]
        }
    }
}


DataContractJsonSerializerでは、匿名型をサポートしていないのに対して、JavaScriptSerializerはイケる。という特徴もありました。ということで、組み合わせの異なる型をガラガラとシリアライズする必要のあるサーバ機能的な物を作る場合、というか4以降ならJavaScriptSerializerがおすすめ。

2013年7月3日水曜日

[Android]ADB server didn't ACKがでて端末がつながらない

最近、Nexus7がつながっているのにもかかわらず、EclipseのDeviceに実機が出てこないことがありました。 で、仕方がないのでADBを再起動するために、コマンドラインでADBを再起動します。 コマンドはこんな感じ。


D:\>adb kill-server

D:\>adb start-server
* daemon not running. starting it now on port 5037 *
ADB server didn't ACK
* failed to start daemon *

という感じで、ADBが再起動できません。何度がkill-serverするとイイみたいなことが書いてあるサイトがありましたが、私の環境では効果がありませんでした。
こういう場合、Windowsの場合タスクマネージャーにadbのプロセスがいるかをチェックします。いる場合、プロセスを停止した後、adb start-serverすると復帰します。
私の環境では、これで回復します。

D:\>adb kill-server

D:\>adb start-server
* daemon not running. starting it now on port 5037 *
* daemon started successfully *



この方法に気が付くまでは、マシン再起動していました。。。。

たぶん、こうなっちゃうのは、Nexus7のUSB端子の接触不良のような気がします。肘などで意図せずその辺を触ってしまうと起こる場合があります。

2013年6月27日木曜日

[Android]SQLiteのカーソル処理ヘルパーを作ってみた

ちょっと前にGDD Blog: [Android]SQLiteによるカーソルのループ処理の書き方っていうのを書きました。なんかしっくりこないので、ヘルパーを書いてみました。
コードはこんな感じ。


/**
 * カーソルのヘルパー.
 */
public class CursorHelper {

    /**
     * 初回フラグ.
     */
    private boolean mIsFirst = true;

    /**
     * 操作対象のカーソル.
     */
    private final Cursor mCursor;

    /**
     * 次有無判定
     *
     * @return true:次あり false:次なし
     */
    public boolean hasNext() {
        if (!mIsFirst) {
            // 2回目以降
            return mCursor.moveToNext();
        }

        mIsFirst = false;
        // 初回
        return mCursor.moveToFirst();
    }

    /**
     * コンストラクタ.
     *
     * @param cursor 操作対象のカーソル
     */
    public CursorHelper(Cursor cursor) {
        mCursor = cursor;
    }
}


で、利用側はこんな感じ。


    public void onButton3(View view) {

        // DBをオープンして、簡単なクエリーを投げる
        SQLiteOpenHelper helper = new DBOpenHelper(this);
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db
                .rawQuery("select * from TEST_TABLE", new String[] {});
        CursorHelper ch = new CursorHelper(cursor);

        // hasNext兼next
        while (ch.hasNext()) {
            // ・・・処理
            Log.d("sql", "code2 data=" + cursor.getString(1));

        }

        cursor.close();
        db.close();
        helper.close();
    }
ヘルパーのhasNextでnextの処理もしなければいけない点と、毎回if文を通る残念な感じはありますが、利用側からはわかりやすいような気もします。 んーイテレータで書いたほうがかっこいいかも。

2013年6月21日金曜日

[Android]try-with-resources風に処理したい

IO系の処理などで、Java1.7の言語仕様で追加されたtry-with-resources構文をつかうとすっきりかけるんですが、現時点で、AndroidのJavaランタイムDalvikの処理系は1.6系です。そのためJava1.7以降に追加された言語仕様を使うことができません。

そこで、Androidで開放漏れを起こしやすい代表的なリソースを開放するヘルパークラスを実装してみました。コードはこんな感じ。


public class Closer {

    private final Stack mTargets = new Stack();

    /**
     * Closeableの登録.
     *
     * @param closeable 対象
     * @return 入力と同じ
     */
    public  T put(T closeable) {
        if (closeable == null) {
            return closeable;
        }
        mTargets.push(closeable);
        return closeable;
    }

    /**
     * SQLiteDatabaseの登録.
     *
     * @param db 対象
     * @return 入力と同じ
     */
    public SQLiteDatabase putDb(SQLiteDatabase db) {
        if (db == null) {
            return db;
        }
        mTargets.push(new SQLiteDatabaseCloser(db));
        return db;
    }

    /**
     * SQLiteDatabaseの登録.
     *
     * @param helper 対象
     * @return 入力と同じ
     */
    public SQLiteOpenHelper putDbHelper(SQLiteOpenHelper helper) {
        if (helper == null) {
            return helper;
        }
        mTargets.push(new SQLiteOpenHelperCloser(helper));
        return helper;
    }

    /**
     * クローズ.
     * 

* 登録と逆順にクローズする. */ public void close() { while (!mTargets.empty()) { try { mTargets.pop().close(); } catch (IOException e) { Log.w("Closer", "Closer#close error!!", e); } } } @Override protected void finalize() throws Throwable { if (!mTargets.isEmpty()) { Log.w("Closer", "Closer#close not call!!"); close(); } super.finalize(); } /** * SQLiteDatabaseのラッパー. */ private class SQLiteDatabaseCloser implements Closeable { private final SQLiteDatabase mTarget; SQLiteDatabaseCloser(SQLiteDatabase target) { mTarget = target; } @Override public void close() throws IOException { mTarget.close(); } } /** * SQLiteOpenHelperのラッパー. */ private class SQLiteOpenHelperCloser implements Closeable { private final SQLiteOpenHelper mTarget; SQLiteOpenHelperCloser(SQLiteOpenHelper target) { mTarget = target; } @Override public void close() throws IOException { mTarget.close(); } } }


finalizeでクローズ処理を実行しているのは保険です。利用側の実装はこんな感じ。


    public void onButton1(View view) {

        byte[] b = "あいうえお".getBytes(Charset.defaultCharset());

        // ヘルパークラスのインスタンスを用意する
        Closer closer = new Closer();

        try {
            // Closeable系のリソースのインスタンスを生成するとき、Closerを経由する
            InputStream in = closer.put(new ByteArrayInputStream(b));
            Writer w = closer.put(new StringWriter());

            byte[] buf = new byte[128];
            in.read(buf);
            w.write(new String(buf, Charset.defaultCharset()));

            // DBOpenHelperはCloseableではないので専用メソッド経由
            SQLiteOpenHelper helper = closer
                    .putDbHelper(new DBOpenHelper(this));
            // SQLiteDatabaseはCloseableではないので専用メソッド経由
            SQLiteDatabase db = closer.putDb(helper.getReadableDatabase());
            Cursor cursor = closer.put(db.rawQuery("select * from TEST_TABLE",
                    new String[] {}));
            if (cursor.moveToFirst()) {
                // 処理
            }

        } catch (IOException e) {
            Log.e("closer", "error!!", e);
        } finally {
            // 内包するリソースを開放する
            closer.close();
        }
    }
try-with-resources程ではないですが、だいぶすっきりするような気がします。tryブロックの前に変数宣言しなくて済むのがいい感じ(個人的な感覚)。。。ですよね?

ちなみにこういったクラスを用意しなかったら、こんな感じ。


   public void onButton1(View view) {

        byte[] b = "あいうえお".getBytes(Charset.defaultCharset());

        InputStream in = null;
        Writer w = null;

        SQLiteOpenHelper helper = null;
        SQLiteDatabase db = null;
        Cursor cursor = null;
        try {
            in = new ByteArrayInputStream(b);
            w = new StringWriter();

            byte[] buf = new byte[128];
            in.read(buf);
            w.write(new String(buf, Charset.defaultCharset()));

            helper = new DBOpenHelper(this);
            db = helper.getReadableDatabase();
            cursor = db.rawQuery("select * from TEST_TABLE", new String[] {});
            if (cursor.moveToFirst()) {
                // 処理
            }

        } catch (IOException e) {
            Log.e("closer", "error!!", e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            if (db != null) {
                db.close();
            }
            if (helper != null) {
                helper.close();
            }
            if (w != null) {
                try {
                    w.close();
                } catch (IOException e) {
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
        }
    }

・・・うんざりですね。

[Android]SQLiteによるカーソルのループ処理の書き方

AndroidでSQLiteからデータを参照する場合、以下のような手順で処理を実装します。
  • SQLiteOpenHelperを生成する
  • SQLiteOpenHelperから、SQLiteDatabaseを取得する(SQLiteOpenHelper#getReadableDatabase()など)
  • SQLiteDatabaseを使ってクSQLを実行する(SQLiteOpenHelper#rawQuery()など)
  • Cursorを使ってデータを取り出す
という手順が一般的だと思います。
上記以外にSQLiteOpenHelperの継承クラスを用意し、DB及びテーブルを準備する実装が別途必要になります。まずは、そのコード例です。

class DBOpenHelper extends SQLiteOpenHelper {

    public DBOpenHelper(Context context) {
        super(context, "test.db"null1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // テーブルの生成
        db.execSQL("CREATE TABLE TEST_TABLE(id INTEGER PRIMARY KEY, memo TEXT NOT NULL, latitude REAL, longitude REAL);");

        // 初期データの投入
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge1')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge2')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge3')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge4')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge5')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge6')");
        db.execSQL("INSERT INTO TEST_TABLE(memo) values('hoge7')");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // nop
    }
}


で肝心のループ処理。Cursor#getCount()を使えば、件数を取得できます。カーソルの処理において、件数を使ってループさせるのはあまりかっこよくない(個人的に)ので、その方式は除外します。
public void onButton2(View view) {

    // DBをオープンして、簡単なクエリーを投げる
    SQLiteOpenHelper helper = new DBOpenHelper(this);
    SQLiteDatabase db = helper.getReadableDatabase();
    Cursor cursor = db
            .rawQuery("select * from TEST_TABLE"new String[] {});

    // 方式1:forを使ったループ
    for (boolean next = cursor.moveToFirst(); next; next = cursor
            .moveToNext()) {
        // ・・・処理
        Log.d("sql""code1 data=" + cursor.getString(1));
    }

    // 方式2:whileを使ったループ
    boolean next = cursor.moveToFirst();
    while (next) {
        // ・・・処理
        Log.d("sql""code2 data=" + cursor.getString(1));

        // 次のレコード
        next = cursor.moveToNext();
    }

    cursor.close();
    db.close();
    helper.close();

}

「方式1:forを使ったループ」は、Effective Javaの項目45の、ローカル変数のスコープを最小化するの方式を使った例です。
この方式では、nextというローカル変数がfor文内部だけのスコープになるので、いくつかのクエリーを実行する場合、ミスは少なくなると思いますが、for文はカウントアップするという感覚があるので(個人的に)、なんか可読性が低い気がします。

「方式2:whileを使ったループ」では、ローカル変数のスコープが広くなるという欠点と、cursor#moveToNext()をループの最後に残し続けなければならないという制約が残ります。そのため修正を行う際、事故が起こりそうな感じはありますが、(個人的に)可読性が良いと考えています。

どちらもビミョーですが、可読性の観点から「方式2:whileを使ったループ」のほうが良いと考えています。

そもそもCursor#hasNext()的なメソッドがあればこんなことにはならなかったのではないかと思います。そうすれば、while (cursor.hasNext())一択になるのに。 なんでないんだろう。。。

2013年6月18日火曜日

[Java][Android]Enum型を使う(Performance Tipsを読み直す)

Android DevelopersのPerformance Tipsには、パフォーマンス/サイズ上の理由からenumの利用を避けるべき。との方針がありました。んで、最新版を読み直してみるとその記述が消えていました。

とあるサイトによると、enumを1つで1KBくらいのサイズが余分に使われる。とのことでした。でも、最近のハードウェアではそんなの微々たるものだよな。と思っていました。
あと、インタフェース型での参照を使わない。っていうのも消えている様子。この辺はAndroidを初めたころ、すごく違和感があったことを記憶しています。

ということで、intからenumに変えるとどんな感じかを書いてみました。
各々の方式でオーディオプレイヤーのステータスを表現するとこんな感じ。

■intで表現した場合
static class Status {
    /** 停止. */
    static final int stop = 0;
    /** 一時停止. */
    static final int pause = 1;
    /** 再生. */
    static final int play = 2;
    /** 早送. */
    static final int next = 10;
    /** 巻戻. */
    static final int prev = 11;
    /** 録音. */
    static final int rec = 20;

    /** 再生中のSet. */
    static final private Set<Integer> STAT_PLAY = new HashSet<Integer>();
    static {
        STAT_PLAY.add(pause);
        STAT_PLAY.add(play);
    }

    /** 再生中. */
    static boolean isPlaying(int stat) {
        return STAT_PLAY.contains(stat);
    }

    /** 停止中. */
    static boolean isStoped(int stat) {
        return stat == stop;
    }

    /** 動作中. */
    static boolean isOperating(int stat) {
        return stat == stop;
    }
}

■enumで表現した場合
public enum Status {
    /** 停止. */
    stop(0),
    /** 一時停止. */
    pause(1),
    /** 再生. */
    play(2),
    /** 早送. */
    next(10),
    /** 巻戻. */
    prev(11),
    /** 録音. */
    rec(20);

    /** 値. */
    private int value = 0;

    /** 再生中のSet. */
    static final private Set<Status> STAT_PLAY = EnumSet.of(pause, play);

    /**
     * コンストラクタ.
     *
     * @param value
     */

    private Status(int value) {
        this.value = value;
    }

    /** 再生中. */
    public boolean isPlaying() {
        return STAT_PLAY.contains(this);
    }

    /** 停止中. */
    public boolean isStoped() {
        return stop == this;
    }

    /** 動作中. */
    public boolean isOperating() {
        return stop != this;
    }

}

利用はこんな感じ。メリット的には

  • タイプセーフ。他の数値を誤って指定できない
  • メソッドが実装できます。 isXXX系を実装すると便利

まぁ、一般論ですね。利用例はこんな感じ。

EnumEnum e = new EnumEnum();
// タイプセーフ。Statusしか渡せない    
e.enumTest(Status.play);
  

public boolean enumTest(Status stat){
    // enumなら、メソッドを実装できる
    return stat.isPlaying();
}


一時期は、利用を控えてみましたが、Performance Tipsから消えたことだし、enumをバンバン使っていく方向で行きましょう。

2013年6月9日日曜日

[Other]EclipseのCheckStyle Pluginで、Unable to instantiate DoubleCheckedLockingが出る

最近Eclipse4系も安定したこともあり、3.7系の環境を捨てて、入れ替えをしました。
で、ちょっと古めのプロジェクトを久々に開いてみると、以下のようなメッセージが。。。

Errors occurred during the build.
Errors running builder 'Checkstyle Builder' on project 'MyProject'.
cannot initialize module TreeWalker - Unable to instantiate DoubleCheckedLocking
cannot initialize module TreeWalker - Unable to instantiate DoubleCheckedLocking
cannot initialize module TreeWalker - Unable to instantiate DoubleCheckedLocking
cannot initialize module TreeWalker - Unable to instantiate DoubleCheckedLocking

どうもCheckstyleでエラーが出ている様子。で調べてみると、↓にたどり着きました。 

http://sourceforge.net/p/checkstyle/bugs/682/

 以下、抜粋。
 DoubleCheckedLocking check has been removed from Checkstyle 5.6, existing configuration files must be adapted (comment/remove check).
 (意訳) Checkstyle5.6でDoubleCheckedLockingは削除されました。なので、設定ファイルからDoubleCheckedLockingを消してね。

 だそうです。で、設定ファイルを見てみると、

    <module name="DoubleCheckedLocking">

 というのがあったので、削除してみると動作しました。

2013年6月1日土曜日

[Other]Eclipse4.2にJD-Pluginを入れる(設定変更が必要)

ソースが提供されていないクローズドなライブラリ(jar)で例外が発生したり、挙動がわからないくて途方に暮れうことってありませんか?

そんな時頼りになるのがデコンパイラ。

昔はJadClipseのを使っていましたが、今はJD-Pluginを使っています。
アップデートサイト
 http://feeling.sourceforge.net/update

しかし、このプラグインEclipse4.2の環境では、そのまま動作しません。設定の変更が必要です。

 Window→Preferences
  General→Editors→File Assosiations
   *.class

の設定をClass File Editorにする必要が有ります。

これをやってしまうと、ソース添付のあるjarがJD-Pluginになっちゃうんじゃない?
と思いますが、ソースが添付されているとそちらを優先していくれるみたいです。なんかよくわかりませんが、貴重な情報が手に入ることには変わりないですね。

っていう情報が、この辺に情報が載っています。
http://stackoverflow.com/questions/11371451/jd-decompiler-for-eclipse-4-2-juno

調査の作業が発生したとき、JD-GUIを別で起動してごにょごにょしてたのが、だいぶ楽になりました。

2013年5月31日金曜日

[Android]AndroidのSQLiteはFULL AUTO VACUUM (auto_vacuum = 1)

普通(Android以外?)で利用するSQLiteでは、データ使用したデータ領域を開放しません。なので、delete/insertを繰り返すと、データが肥大化するため、フラグメンテーションを解消する必要があります。そのコマンドが、Vacuumです。 たとえば、日に1度データの入れ替えを行うために、全マスタをdeleteしてサーバから最新をとってinsertするというようなシステムでは、フラグメンテーションを解消しないと、性能的にも問題が発生する恐れがあります。そのため、Vacuumするのですが、先日TLを眺めていると「AndroidのSQLiteはauto vacuumだよー」的な発言が。。。 

「ウソだろ!」と思って、手元にある2.3系と4.2系で以下のようなコードで確認してみました。
コードはこんな感じ。


        DBHelper helper = new DBHelper(this);
        SQLiteDatabase database = helper.getReadableDatabase();
        Cursor cursor = database.rawQuery("PRAGMA auto_vacuum"null);

        cursor.moveToFirst();
        Log.d("tag""auto_vacuum = " + cursor.getString(0));


で、実行結果は2.3/4.2の両方とも
05-31 23:44:22.833: D/tag(19169): auto_vacuum = 1

auto_vacuum は0:NONE 1:FULL 2:INCREMENTALらしいです。そのため、自動的にVacuumされます。よってVacuumする必要はまったくありません。。。

参考情報はこの辺
http://tools.oesf.biz/android-2.3_r1.0/xref/external/sqlite/dist/Android.mk
http://tools.oesf.biz/android-2.3_r1.0/xref/external/sqlite/dist/sqlite3.c

2013年5月28日火曜日

[Java]文字列の16進数変換&復元

ソケットでバイナリ通信する場合や、バイナリ形式のファイルを操作するときがあります。 で、その処理がうまくいっているかをダンプしたい時って結構あると思います。
そんな時、.NETであれば「[.NET]文字列の16進数変換&復元」でConvertというクラスで簡単に操作できます。で、Javaでそんなクラスってないかなぁ?って調べてみたらありました。

バイト列と文字列の相互変換って結構あるので、こういうのがあると助かるんですよね。Java1.6から利用可能なので、大半の仕事で適用可能ですね。
コードはこんな感じ。


public static void main(String[] args) {

    // 文字列からUTF8のバイト列に変換
    byte[] data = "1192つくろう鎌倉幕府!!".getBytes();

    // 001122形式の文字列に変換
    String hexText = DatatypeConverter.printHexBinary(data);
    System.out.println("16進文字列 [" + hexText + "]");

    // UTF8のバイト列からstringに変換
    String decText = new String(DatatypeConverter.parseHexBinary(hexText));
    System.out.println("復元結果 [" + decText + "]");
}

■実行結果
16進文字列 [31313932E381A4E3818FE3828DE38186E98E8CE58089E5B995E5BA9CEFBC81EFBC81]
復元結果 [1192つくろう鎌倉幕府!!]

・・・しかし、AndroidのSDKには含まれません。。ということで参考になるソースはこちら。 DatatypeConverterImpl

[Java]ストリームを転送する

ファイルからファイルであれば、[Java]ファイルを簡単にコピーするでOKですが、適当な入力ストリームから出力ストリームに出力したい。みたいな時があります。
たとえば、特定のURLからファイルをダウンロードしてファイルに保存したいみたいなケースですね。単純なのでその場で書いてしまうケースが多いですが、それをクラス化してみました。

// try-with-resourcesをサポートするためにjava.io.Closeableをインプリメント
public class StreamTransmitter implements Closeable {

    final private InputStream input;
    final private OutputStream output;
    final int bufSize;

    public StreamTransmitter(InputStream is, OutputStream os) {
        input = is;
        output = os;
        bufSize = 2048;
    }

    public void transfer() throws IOException {
        byte[] buf = new byte[bufSize];

        int read;
        while ((read = input.read(buf)) != -1) {
            output.write(buf, 0, read);
        }
        output.flush();
    }

    @Override
    public void close() throws IOException {

        // 内包するStreamもCloseableなのでtry-with-resourcesでOK
        try (InputStream in = input; OutputStream out = output;) {
        }
    }
}

このクラスを利用した例。
public static void main(String[] args) {

    String in = "/temp/LICENSE.txt";
    String out = "/temp/LICENSE_OUT.txt";

    try (StreamTransmitter st = new StreamTransmitter(new FileInputStream(
            in), new FileOutputStream(out))) {

        st.transfer();

        // try-with-resourcesなので、明示しなくてもOK
        st.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

比較的簡単ですね。

2013年5月21日火曜日

[Java]一定時間経過してから逐次処理を実行する(Queue)

処理をキューイングして、逐次処理をする。よくあるパターンです。一般的にこういう処理を実装するのにはQueue<E>を使うと思います。
では、キューに入れるけど、一定時間経過してからしか処理したい。または、連打された場合、最後の処理だけ実行したい。みたいなことがあると思います。
こういう時に、BlockingQueue<E>を使います。BlockingQueueはインタフェースです。そのため、その実装であるjava.util.concurrent.DelayQueue<E>を使います。

でも、addできるのは、java.util.concurrent.Delayedをインプリメントしたクラスだけ。

実装した例は以下のような感じ。


    public static void main(String[] args) throws InterruptedException {

        long[] waits = { 1086012 };
        String[] tests = { "10""8""6""0""1""2" };

        // 遅延時間のばらばらのデータをqueueに入れる
        BlockingQueue<Task> queue = new DelayQueue<Task>();
        for (int i = 0; i < waits.length; i++) {
            queue.add(new Task(tests[i], waits[i]));
        }

        // 空になるまでループする。
        while (!queue.isEmpty()) {
            Task task = queue.take();
            task.execute();
        }

    }

    /**
     * java.util.concurrent.Delayedを実装した例.
     * 
     */

    static class Task implements Delayed {

        final private long execTime;
        final private String text;

        /**
         * テキストと遅延時間を指定したコンストラクタ.
         * 
         * @param text
         * @param wait
         */

        Task(String text, long wait) {
            this.text = text;
            this.execTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(wait);
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.lang.Comparable#compareTo(java.lang.Object)
         */

        @Override
        public int compareTo(Delayed o) {
            long diff = execTime - ((Task) o).execTime;
            if (diff == 0) {
                return 0;
            }

            return (diff > 0) ? 1 : -1;
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * java.util.concurrent.Delayed#getDelay(java.util.concurrent.TimeUnit)
         */

        @Override
        public long getDelay(TimeUnit unit) {
            // 残遅延時間を返却
            System.out.println(String.format("%tT getDelay text=[%s] w=[%d]",
                    new Date(), text, execTime - System.nanoTime()));
            return execTime - System.nanoTime();
        }

        /**
         * 処理.
         */

        public void execute() {
            System.out.println(String.format("%tT execute  text=[%s]秒後",
                    new Date(), text));
        }

    }

■実行結果
00:51:24 getDelay text=[0] w=[-910031]
00:51:24 execute text=[0]秒後
00:51:24 getDelay text=[1] w=[892204037]
00:51:25 getDelay text=[1] w=[-1095530]
00:51:25 execute text=[1]秒後
00:51:25 getDelay text=[2] w=[998366343]
00:51:26 getDelay text=[2] w=[-1155035]
00:51:26 execute text=[2]秒後
00:51:26 getDelay text=[6] w=[3995886907]
00:51:30 getDelay text=[6] w=[-13674573]
00:51:30 execute text=[6]秒後
00:51:30 getDelay text=[8] w=[1985765928]
00:51:32 getDelay text=[8] w=[-11499437]
00:51:32 execute text=[8]秒後
00:51:32 getDelay text=[10] w=[1986226602]
00:51:34 getDelay text=[10] w=[-11083182]
00:51:34 execute text=[10]秒後

Queueに登録する順は関係ありませんが、キューの処理順は、compareTo()メソッドの戻り順で処理されます。そのため、遅延時間によって処理順を決めています。 「execute」とログが出ている行を追っかけていくと、指定した通りの時間で処理されていますね。 ログをそこだけに絞り込むと、以下のようになります。遅延指定した時間に実行されています。
00:51:24 execute text=[0]秒後
00:51:25 execute text=[1]秒後
00:51:26 execute text=[2]秒後
00:51:30 execute text=[6]秒後
00:51:32 execute text=[8]秒後
00:51:34 execute text=[10]秒後

2013年5月16日木曜日

[Java]Mapで指定キーに近い値を得る

SortedSet<E>は、指定の値の近似値を取得することができます。APIリファレンスを見ると、似たような名前のMapがあります。 それが、SortedMap<K,V>です。
名称からSortedSet<E>と似たようなことができるんじゃないか?と思ってリファレンスを見てみると、想定通りインタフェースがありました。 確認すると、NavigableMap<K,V>というインタフェースがあり、ここに定義されていました。関係するメソッドは以下の通り。 これらを式で書いてみました。

  • lowerEntry(K key) : K < key
  • floorEntry(K key) : K <= key
  • ceilingEntry(K key) : K >= key 
  • higherEntry(K key) : K > key 
これ以外にも、先頭や最後とるとか、便利そうなメソッドが提供されています。

指定の値以上のデータをループしてとる。みたいなことをやりたい場合はこんな感じ。

    public static void main(String[] args) {

        NavigableMap<Integer, String> map = new TreeMap<Integer, String>();
        for (int i = 1; i < 10; i++) {
            int key = (int) Math.pow(2, i);
            String value = String.format("key is %d", key);
            map.put(key, value);
        }
        // [2, 4, 8, 16, 32, 64, 128, 256, 512]

        int next = 50;
        Map.Entry<Integer, String> entry;
        while ((entry = map.higherEntry(next)) != null) {
            System.out.printf("key=%d value=%s\n", entry.getKey(), entry.getKey());
            next = entry.getKey();
        }
    }

key=64 value=64
key=128 value=128
key=256 value=256
key=512 value=512

2013年5月15日水曜日

[Java]更新できないSetをメンバ変数で初期化する

何かの照合用にメンバー変数にSetを持っておきたいことって時々あると思います。
でも引数付のコンストラクタがないので、インスタンスイニシャライザやスタティックイニシャライザで処理することは多いと思います。でも定義と宣言が分かれてしまうので、ちょっとわかりづらくなるケースがあります。

というのを組みわせで解決してみました。がこんな感じ。

// 更新できないSetをメンバ変数として生成する
private final Set<String> set = Collections.unmodifiableSet(
        new HashSet<String>(
                Arrays.asList(
                        "aaa",
                        "bbb",
                        "ccc",
                        "ddd"
                )
            )
        );

ていうか、素直にユーティリティ系のメソッドを準備すべきですよね。
    private final Set<Integer> set2 = newUmSet(
            1,
            2,
            3,
            4
    );

    @SafeVarargs
    static <E> Set<E> newUmSet(T... args){
        Set<E> set = new HashSet<E>(Arrays.asList(args));
        return Collections.unmodifiableSet(set);
    }

2013年5月12日日曜日

[Android]FragmentTransactionをロールバックする


そういえば。FragmentTransactionのrollbackってどうやってやるんだろう?
と思ってFragmentTransactionのリファレンスを見てみました。
きっとrollbackみたいなメソッドがあるんだろうなぁ。と思って眺めていても見つからない。
で、いろんなサイトを見渡してみると、
 ロールバック=FragmentTransaction#commitを呼ばない。
ということらしい。

「トランザクション=DB」みたいな感覚があるので、commitもrollbackもしないなんて、なんか気持ち悪いなぁと思って調べてみました。

一般的にFragmentの操作は以下のように行います。
  • FragmetnManarerを取得する
  • FragmetnManarer#beginTransactionでFragmentTransactionを取得する
  • FragmentTransaction#addなどで、Fragmentの操作を行う
  • FragmentTransaction#commitを呼ぶ→UIが切り替わる
です。

調べてみると、FragmentTransactionの実装クラスBackStackRecordでは、Fragmentの操作をリストで管理しており、実装は以下のようになっていました。
  • add/replase/deleteが呼ばれるたびに、内部のリンクリストへ情報を追加
  • commitでためていたリストをFragmentManagerへ渡す
  • FragmentManagerはHandler経由でUI処理
という感じでした。

一口にトランザクションって言っても実装方法はそれぞれですが、インタフェースとしての対称性としてどうなのか?と思ってしまいます。
  • FragmetnManarerでbeginTransactionするのに、commitはFragmentTransaction
  • FragmentTransactionにもFragmetnManarerにもrollbackがない
デザイン的に、Builderです。という風に言われるとなるほどそうかもしれない。と思えます(クラス名的にダメじゃないと思いますが)。
たとえばAlertDialog.Builderでいろいろ値を設定し、createやshowを呼ばなかったら。と同じですね。

2013年5月6日月曜日

[Java]コードから動的にinstanceofしたい

特定の型を継承または実装しているオブジェクトに対して、何等か処理をしたい場合、判定にinstanceofを使います。 しかし、instanceofはJavaの構文であるため、動的に判定することはできません。

何とかする方法ないかなぁと調べてみると、以下のような方法がありました。Class.isInstanceかisAssignableFromを使います。
public static void main(String[] args) {

    Object baseClass = new BaseClass();
    Object subClass = new SubClass();

    Object baseClassWithSubOthreType = new BaseClassWithSubOthreType();
    Object subClassWithSubOthreType = new SubClassWithSubOthreType();

    // instance of
    System.out.println("baseClass is Object=" + (baseClass instanceof Object));
    System.out.println("baseClass is BaseClass=" + (baseClass instanceof BaseClass));
    System.out.println("baseClass is SubClass=" + (baseClass instanceof SubClass));
    System.out.println("baseClass is BaseOthreType=" + (baseClass instanceof BaseOthreType));

    System.out.println("subClass is Object=" + (subClass instanceof Object));
    System.out.println("subClass is BaseClass=" + (subClass instanceof BaseClass));
    System.out.println("subClass is SubClass=" + (subClass instanceof SubClass));
    System.out.println("subClass is BaseOthreType=" + (subClass instanceof BaseOthreType));

    System.out.println("baseClassWithSubOthreType is Object=" + (baseClassWithSubOthreType instanceof Object));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (baseClassWithSubOthreType instanceof BaseClass));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (baseClassWithSubOthreType instanceof SubClass));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + (baseClassWithSubOthreType instanceof BaseOthreType));

    System.out.println("subClassWithSubOthreType is Object=" + (subClassWithSubOthreType instanceof Object));
    System.out.println("subClassWithSubOthreType is BaseClass=" + (subClassWithSubOthreType instanceof BaseClass));
    System.out.println("subClassWithSubOthreType is SubClass=" + (subClassWithSubOthreType instanceof SubClass));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + (subClassWithSubOthreType instanceof BaseOthreType));

    // isInstance
    System.out.println("-----------");
    // 比較対象のクラスが自クラスの型に対してい代入可能か判定する
    System.out.println("baseClass is Object=" + Object.class.isInstance(baseClass));
    System.out.println("baseClass is BaseClass=" + BaseClass.class.isInstance(baseClass));
    System.out.println("baseClass is SubClass=" + SubClass.class.isInstance(baseClass));
    System.out.println("baseClass is BaseOthreType=" + BaseOthreType.class.isInstance(baseClass));

    System.out.println("subClass is Object=" + Object.class.isInstance(subClass));
    System.out.println("subClass is BaseClass=" + BaseClass.class.isInstance(subClass));
    System.out.println("subClass is SubClass=" + SubClass.class.isInstance(subClass));
    System.out.println("subClass is BaseOthreType=" + BaseOthreType.class.isInstance(subClass));

    System.out.println("baseClassWithSubOthreType is Object=" + (Object.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (BaseClass.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (SubClass.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isInstance(baseClassWithSubOthreType));

    System.out.println("subClassWithSubOthreType is Object=" + Object.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is BaseClass=" + BaseClass.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is SubClass=" + SubClass.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isInstance(subClassWithSubOthreType));

    // isAssignableFrom
    System.out.println("-----------");
    // 比較対象のクラスが自クラスのサブクラスか判定する
    System.out.println("baseClass is Object=" + Object.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is BaseClass=" + BaseClass.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is SubClass=" + SubClass.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(baseClass.getClass()));

    System.out.println("subClass is Object=" + Object.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is BaseClass=" + BaseClass.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is SubClass=" + SubClass.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(subClass.getClass()));

    System.out.println("baseClassWithSubOthreType is Object=" + (Object.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (BaseClass.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (SubClass.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(baseClassWithSubOthreType.getClass()));

    System.out.println("subClassWithSubOthreType is Object=" + Object.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is BaseClass=" + BaseClass.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is SubClass=" + SubClass.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(subClassWithSubOthreType.getClass()));


}

// BaseClass
static class BaseClass {

}

// SubClass
static class SubClass extends BaseClass {

}

// BaseClass + SubOthreType
static class BaseClassWithSubOthreType implements SubOthreType {

}

// SubClass + SubOthreType
static class SubClassWithSubOthreType extends SubClass implements SubOthreType {

}

// BaseOthreType
interface BaseOthreType {

}

// SubOthreType
interface SubOthreType extends BaseOthreType {

}

■出力結果(どれも同じ結果が出ます)
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true
-----------
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true
-----------
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true

・・・そもそもタイプ別で分岐しないといけないというのは設計が悪い。というケースが多いのかなぁと思います。 しかし、実装レベルで楽をするために。というのはありかなぁと思います。
たとえば、例外発生時のエラーメッセージの判定を一括して行う処理を実装する場合、これをMap<Exceptionのラッパー,String>で処理できるとずいぶんすっきりすると思います。

キーになる情報のhashcode()を工夫すれば、楽に実装できるような気がします。 instanceofだとif文たくさんになる図が目に浮かびますね。

しかし、この2つは、何が違うんでしょう?リファレンスを見ると、
  • isInstanceは、Java 言語の instanceof 演算子と動的に等価
  • isAssignableFromは、型として等しいか。プリミティブのタイプも判定できる
ということらしいです。
・・・じゃあ、相互互換ってどうなんだろう?ということでやってみました。コードと結果は以下の通り

    System.out.println("Integer isInstance int = " + int.class.isInstance(new Integer(1)));
    System.out.println("int isInstance Integer = " + Integer.class.isInstance(1));

    System.out.println("Integer isAssignableFrom int = " + int.class.isAssignableFrom(Integer.class));
    System.out.println("int isAssignableFrom Integer = " + Integer.class.isAssignableFrom(int.class));

--以下出力結果

Integer isInstance int = false
int isInstance Integer = true
Integer isAssignableFrom int = false
int isAssignableFrom Integer = false

Class.isInstanceでは、双方向でtrueを予想していたのですが、Integerとintは互換があり、逆は互換がない。っていう結果になりました。
Class.isAssignableFromに関しては、両方false。理由がよくわからない。。。

2013年5月5日日曜日

[java]メソッドで配列化する

javaでその辺にある引数やローカル変数で配列を作る場合に、new 型[]{arg1, arg2,arg4};とするのが一般的です。
その他の方法として、可変長引数を使ってメソッド化する方法があります。コードはこんな感じ。
public static void main(String[] args) {
// Stringの場合
String[] stringArray = Converter.convert("a""b""c");
// 数値はラッパー型になる。intで処理できないのが残念。。。
Integer[] intArray = Converter.convert(123);
// オーバーロードすればそれなりに

}

public static class Converter {
static <T> T[] convert(T... args) {
    // テンプレート型指定でキャスト不要
    // 引数をそのまま返すだけ!
    return args;
}
この方式で何かいいことあるか?といわれると。。。とくにありません。

[Java]C#のusingのような構文でリソースを開放する(try-with-resources)

時代はJava8に突入しようとしていますが、Java7の話題です。。。
C#のusingは、リソースを開放するのがすごく楽になる構文です。 コード的にはこんな感じ。
class Program
{
    static void Main(string[] args)
    {
        using (Hoge1 hoge1 = new Hoge1())
        {
            hoge1.execute();
        }
    }
}

class Hoge1 : IDisposable
{
    public void execute()
    {
        Console.WriteLine("Hoge1 execute");
    }

    public void Dispose()
    {
        Console.WriteLine("Hoge1 Dispose");
    }
}

Java(6)と比べて非常にすっきりしています。 そのJava6では、こんな感じで非常に残念。
public static void main(String[] args) {
    Hoge1 hoge = null;
    try {
        hoge = new Hoge1();
        hoge.execute();
    } finally {
        if (hoge != null) {
            try {
                hoge.close();
            } catch (IOException e) {
                // ほとんどのケースで、ここで例外が出てもどうしようもない。。。
                // 処理的には割り切りでしょうか。
            }
        }
    }
}

でもJava7のtry-with-resourcesだとこんな感じ。以下の例では、複数のAutoCloseableに対応した場合です。
public static void main(String[] args) {
    try (Hoge2 hoge2 = new Hoge2(); Hoge1 hoge1 = new Hoge1();) {
        hoge1.execute();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

static class Hoge1 implements AutoCloseable {

    public void execute() {
        System.out.println("Hoge1 execute");
    }

    @Override
    public void close() throws IOException {
        System.out.println("Hoge1 close");

        throw new IOException("Hoge1 close");
    }

}

static class Hoge2 implements AutoCloseable {

    public void execute() {
        System.out.println("Hoge2 execute");
    }

    @Override
    public void close() throws IOException {
        System.out.println("Hoge2 close");
    }
}

try構文の特徴も引き継ぐので、ブロック内にcatch文を書くこともできます。
(これ、地味ですが微妙にいい感じ)

C#のusingで複数のIDisposableに対応した書き方をすると、以下の通り。
    using (Hoge1 hoge1 = new Hoge1())
    using (Hoge2 hoge2 = new Hoge2())
    {
        hoge1.execute();
        hoge2.execute();
    }

当時、usingを複数書く構文には違和感を覚えましたが、すっかり慣れてしまいました。あと、import分に対応する文がusingであり、そこも微妙に違和感がありました。
Javaのtry-with-resourcesでは、1つのブロック中に記載するので、逆に違和感が。。。

2013年5月3日金曜日

[Android]設定アプリの「データを削除」を無効化する

Androidのアプリケーションでは、データベースやプレファレンスでデータを保存します。
 たとえば、SQLiteのデータベースの場合一般的には「/data/data/[アプリケーションパッケージ名]/databases」にファイルが生成されます。 しかし、このデータは、設定アプリで削除することができます。 それを抑止する方法として、その処理を無効化する方法がります。その方法とは以下の通りです。

 ■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.your_app"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:manageSpaceActivity="com.example.your_app.ManageActivity" >
        <activity
            android:name=".GridLayoutActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

「android:manageSpaceActivity」を指定することで、設定アプリがこのアクティビティ呼ぶようになります。 ボタンの表示も「データを消去」から「容量を管理」に表現が変わりました(端末によって差はあるかもしれません)。

そのため 、onCreate()でfinish()しちゃえば、ほぼ何も起こらない。ということが実現できます。

逆に
  • SDカードに管理しているデータもまとめて消す
  • プレファレンスだけ残してデータだけです
みたいかことも可能です。応用範囲はいろいろ。企業ユースでは結構ニーズがあるかもしれません。

2013年4月30日火曜日

[Java]Instance Initializerっていうのがあります

Javaで、インスタンスを初期化するのはコンストラクタですが、コンストラクタと同等の初期化を実施することができる、Instance Initializerというのがあります。コードはこんな感じ。
public static void main(String[] args) {

    new ExtendClass();

}

static class BaseClass {

    {
        System.out.println("BaseClass{}1");
    }

    {
        System.out.println("BaseClass{}2");
    }

    public BaseClass() {
        System.out.println("BaseClass()");
    }
}

static class ExtendClass extends BaseClass {

    {
        System.out.println("ExtendClass{}1");
    }

    public ExtendClass() {
        System.out.println("ExtendClass()");
    }

    {
        System.out.println("ExtendClass{}2");
    }
}
普通のメソッド同様に、ローカル変数やsuper/thisなどのキーワードを使うことができます。

■実行結果
BaseClass{}1
BaseClass{}2
BaseClass()
ExtendClass{}1
ExtendClass{}2
ExtendClass()

Static InitializerもInstance Initializerも複数個所に分断してかけますが、分断するとわかりづらくなります。 しかし、以下のような初期化の仕方はありじゃないでしょうか。 個人的な感覚ですが、コレクションの初期化については、コンストラクタや統一ブロックで処理するよりわかりやすくなると思います。
static class SetInitClass {

    final private Collection<Integer> set1;
    {
        Set<Integer> set = new HashSet<Integer>();
        set.add(1);
        set.add(3);
        set.add(5);
        set1 = Collections.unmodifiableCollection(set);
    }

    final private Collection<Integer> set2;
    {
        Set<Integer> set = new HashSet<Integer>();
        set.add(2);
        set.add(4);
        set.add(6);
        set2 = Collections.unmodifiableCollection(set);
    }

}