2013年4月30日火曜日

[Java]static initializerの初期化タイミング

static initializerは便利ですが、思わぬ落とし穴があります。それは実行タイミングです。 たとえば、以下のようなコードがあった時、static initializerはどこで実行されるでしょう?
public static void main(String[] args) {

    System.out.println("0000");
    String aaa = StaticInitClass.aaa;
    System.out.println("1111");
    String bbb = StaticInitClass.bbb;
    System.out.println("2222");
    new StaticInitClass();

}

static class BaseStaticInitClass {
    public static String aaa = "aaa";

}

static class StaticInitClass extends BaseStaticInitClass {

    public static String bbb = "bbb";

    static {
        System.out.println("StaticInitClass{}1");
    }

    static {
        System.out.println("StaticInitClass{}2");
    }

    StaticInitClass() {
        System.out.println("StaticInitClass()");
    }
}

■実行結果は以下の通り。
0000
1111
StaticInitClass{}1
StaticInitClass{}2
2222
StaticInitClass()

「String aaa = StaticInitClass.aaa;」の時点でstatic initializerが実行されてほしいんですが、実行されません。 これは、StaticInitClassの親クラスBaseStaticInitClass へのアクセスであり、StaticInitClassに対しての操作ではありません。 そのため、static initializerは呼び出されないのです。

私は、singletonのインスタンスBaseStaticInitClass相当のクラスに隠ぺいしようとして、static initializerが呼ばれず、NPEという残念な結果になりました。
というような感じでにstaticな処理を隠ぺいしようとした場合、注意が必要です。

[Java]static initializerは複数書ける?

これもコードレビューネタ。
少し前なんですが、Javaでstaticブロック(static initializer)で変数を初期化している処理があったんですが、なんと、複数個所にstatic{}があるじゃないですか!コード例はこんな感じ。
static class StaticInitClass {

    static {
        System.out.println("StaticInitClass{}1");
    }

    static {
        System.out.println("StaticInitClass{}2");
    }

    StaticInitClass() {
        System.out.println("StaticInitClass()");
    }
}

■出力結果
StaticInitClass{}1
StaticInitClass{}2
StaticInitClass()

こんなコードが書けるとは。。。固定観念でした。

2013年4月27日土曜日

[Java]集合の演算

Set<T>のことが気になってインタフェースを見直ししてみました。
今までは、重複不可のリストくらいに思っていましたが、これよく見ると、演算可能な集合を実装したクラスなんですね。
たとえば、
  • 足し算:addAll(Collection<? extends E> c) 
  • 引き算:removeAll(Collection<?> c) 
  • AND:retainAll(Collection<?> c) 
  • OR:=足し算 
という集合同士の演算が可能です。組み合わせればXORとかも作れそうですね(addAll→retainAll→removeAll)。
そして集合に対する評価として
  • ALL:containsAll(Collection<?> c) とcontains(Object o)
  • ANY:!Collections.disjoint(Collection<?> c1, Collection<?> c2) 
なんでANY(いずれかが一致)がないんだろう。。。
と思ったら、Collectionsにdisjoint(Collection<?> c1, Collection<?> c2) というメソッドがあります。
このメソッドは、引数の2つのコレクションを比較し同じものがない場合trueを返します。
つまりその結果をNotすれば、containsAnyになる。ということですね。

[java]ある集合内で近い値を求める

たとえば、1,2,4,8,16,32みたいな配列があって、その中から10に近い値を求めたい。みたいなことがあると思います。そんな時はTreeSet<T>を使います。
コードはこんな感じ。

public static void main(String[] args) {

    TreeSet<Integer> treeSet = new TreeSet<Integer>();
    for (int i = 1; i < 10; i++) {
        treeSet.add((int) Math.pow(2, i));
    }

    //[2, 4, 8, 16, 32, 64, 128, 256, 512]
    System.out.println("floor 10 -> " + treeSet.floor(10));
    System.out.println("floor 20 -> " + treeSet.floor(20));
    System.out.println("floor 64 -> " + treeSet.floor(64));
    System.out.println("ceiling 10 -> " + treeSet.ceiling(10));
    System.out.println("ceiling 20 -> " + treeSet.ceiling(20));
    System.out.println("ceiling 64 -> " + treeSet.ceiling(64));

}

■結果
floor 10 -> 8
floor 20 -> 16
floor 64 -> 64
ceiling 10 -> 16
ceiling 20 -> 32
ceiling 64 -> 64

TreeSet<T>なんて、ソートしてくれるだけじゃ?と思っていたら大間違いでした。。。
これ以外にも
  • 指定数未満の集合を得る:headSet(E toElement) 
  • 指定数より大きい値の集合を得る:tailSet(E fromElement) 
  • 指定数以上、指定数未満の集合を得る:subSet(E fromElement, E toElement) 

など、便利そうなやつが結構ある。
ほかのコレクションクラスのリファレンスも改めて見直ししたくなりました。

2013年4月24日水曜日

[Java][.NET]クラス内でthis.member(メンバ変数)は冗長(改め?)

時々、こういうコーディング見かける時があります。下でいう、makeUniqId()のように、メンバ参照するときにthisつけることを指しています。

class User {
    final private String userId;
    final private String userName;

    public User(String id, String name) {
        this.userId = id;
        this.userName = name;
    }

    public String makeUniqId(String separator) {
        return this.userId + separator + this.userName;
    }
}


正直冗長じゃね?ってずーと思ってきたんですが、実はこれはプレフィックス代わりに使ってるんです。っていうのを何かの文献で発見しました。
これをみて、アアッてなりました。
・・・プレフィックス。ハンガリアンなんてダサいゼって言われ始めてからはや10年以上たちます。こう書かれたときはメンバ変数だってことが一目でわかる。意外といい方法?なのかもしれません。

(ただし、メソッド呼び出すときにthisつけるのは冗長だよね。というのは変わっていない)

2013年4月19日金曜日

[Android]Fragmentはバックグラウンドスレッドから操作できる

AndroidはUIを操作をメインスレッドのみで行うシングルスレッドモデルで設計されています。 バックグラウンドスレッドから、UIの操作を行う時は、Handler経由で行うのが定石とされています。

 で、今日、コードレビューしていると、バックグラウンドスレッドの中で、DialogFragmentを表示する処理を書いているのを見つけました。 ってかこれだめだよね。と思って動作させてみると、普通にダイアログが出てくる。 なんで例外でないんだろう?と思って調べてみると、まわりまわってHandler経由でUIの操作をしているのがわかりました。

流れはこんな感じ。
  1. FragmentManager#beginTransaction→(FragmentManagerImpl#beginTransaction)
  2. (Fragmentをいろいろ操作)
  3. FragmentTransaction#commit→(android.app.BackStackRecord#commit)
  4. (FragmentManagerImpl#enqueueAction)→(Activity#mHandlerに処理委譲)
つまり、バックグラウンドスレッドからFragmentを切り替えたとしても、Handlerを経由してメインスレッドで実行される。ということです。

これの何が嬉しいかというと、処理全体をバックグラウンドスレッドにできるという利点があります。
たとえば、定期的にデータを取得して画面を更新するみたいな処理は、デーモンスレッドを1つ起こしておいて動かしっぱなし。というのが可能です。そしてFragmentは画面の一部を更新するのに向いています(たぶん更新はreplaseしかダメかなぁ)。

 それとは別の観点では、Androidは通信処理をバックグラウンドスレッドで実行する必要があります。そのため、
  • 通信はバックグラウンド
  • UI更新はメインスレッド
のようなめんどくさいことをやるため、AsyncTaskやLoaderを使います。
AsyncTaskではコールバックでメインスレッドとバックグラウンドスレッドを呼び分けてくれますが、処理が分断されるため、一連の流れがわかりづらくなるというデメリットがあります。
使ったことがある人はわかると思いますが、実装上のコードのネストがどうしても深くなってしまいます。

しかし、すべてをバックグラウンドで処理できるとなると、たとえばボタン押下以降の処理全体をバックグラウンドスレッドで実行することが可能になります。
私はHandlerTheadを結構使います。このクラスは、以下のような特徴があります。
  • バックグラウンドスレッドである
  • Queueで処理するため、一連の順序性が保たれる(逐次処理ができる)
  • 遅延実行できる
  • Queueをキャンセルすることができる
しかし、ここからUIを操作するのがめんどくさい(Handler経由になる=HandlerのくせにHandler持ってないとダメ)ので完全にバックグラウンドの逐次処理だけに限って使っていましたが、これがありなら、

 通信→データ加工→UI更新

みたいな一連の流れをカプセル化し処理することが可能になります。

しかし、実装上こうなっているだけで、これをやってOKとはどこにも書かれていません。将来的にこの実装のまま。というわけではないので、注意は必要ですね。
OSのバージョンを固定してもいい場合(一品もの)などは、この方式もありかもしれません。。。

2013年1月5日土曜日

[Android]SQLiteで実行計画を取得する

SQLServerやOracleでは、実行するクエリーに対してどれくらいのコストがかかるかを評価することができます。で、Androidで利用されるSQLiteでは?とおもって調査してみました。 実は簡単で、実行するクエリーの前に、「explain」ってつけるだけ。
コードはこんなかんじ。
explain select * from TEST_TABLE
で、実行結果はというと、こんな感じ。
addr    opcode    p1    p2    p3    p4    p5    comment
0    Trace    0    0    0        00    null
1    Goto    0    14    0        00    null
2    OpenRead    0    4    0    4    00    null
3    Rewind    0    12    0        00    null
4    Rowid    0    1    0        00    null
5    Column    0    1    2        00    null
6    Column    0    2    3        00    null
7    RealAffinity    3    0    0        00    null
8    Column    0    3    4        00    null
9    RealAffinity    4    0    0        00    null
10    ResultRow    1    4    0        00    null
11    Next    0    4    0        01    null
12    Close    0    0    0        00    null
13    Halt    0    0    0        00    null
14    Transaction    0    0    0        00    null
15    VerifyCookie    0    2    0        00    null
16    TableLock    0    4    0    TEST_TABLE    00    null
17    Goto    0    2    0        00    null
・・・みかたがさっぱりわからない。今度調査してみよう。