2015/10/10

[C]externは怖い

仕事ではC#とC/C++、趣味でJavaを使っています。
それぞれ用途に合わせて使っているので混乱することは意外とないのですが、それでもおかしなことをやらかすことはあります。

Cで何気なく書いた、絶対安全だと思っていたコードがクラッシュを引き起こしました

//////data.c////////

const char table[1]={1};

//////logic.c////////

extern const char* table;

if(table[0]==1){

//////////////


運用上の理由があり、どうしてもデータとロジックを分ける必要がありました。
かなりコードサイズに制約がある組込ファームウエアなので、constの定数についてはアクセス関数を使わずに直接externでリンクすることを許していました。
externをヘッダに書かなかったのは、絶対にこの場所でしか参照しないデータで、かつ第三者に存在を公開するのを避けたかったからです

私はtable[]とtable*は表現の違いだけで、同じものだと思っていました
だって、これが成り立つのは自明なので

///////////////

const char table[1]={1};

const char*p = table;

if(p[0]==1){

///////////////

さらに、これならクラッシュしないことも確認しました

//////data.c////////

const char table[4]={1,2,3,4};

//////logic.c////////

extern const char* table;

if(table[0]==1){

//////////////

添え字が1の時はコンパイラが勝手に最適化して定数として展開するのでしょうか?それはおかしい。だってデータ数を知っているのはdata.cの方で、logic.cはサイズを知らないのだから、配列サイズによって振る舞いを変えるということはできないはずです。

こうすればクラッシュしないこともわかりました

//////data.c////////

const char table[1]={1};

//////logic.c////////

extern const char table[];

if(table[0]==1){

//////////////

いよいよわからなくなってきました。
なにしろ私は[]と*はバイナリにしたら同じだと思っていましたから。
逆アセンブルしてみると、バイトコードには差異がありました。
なんだ、この差は?

…純粋なCプログラマの方々はおそらく私が何をいってるんだろうかと失笑していることでしょうね。はい、私も気が付いた時には笑いました。

まず第一に[]と*は違うものです。
説明には諸説あるようですが、*がポインタを表すのはおなじみだと思いますが、table[]は配列をあらわし、tableと書くと配列の先頭のアドレスを指すというルールがあるもののtableがポインタになっているわけではないのです。
それが証拠に下の式は成り立ちませんよね?

//////////////

const char table1[]={1};
const char table2[]={2};

table1 = table2;

//////////////

配列はポインタに代入できるが、ポインタではない。extern const*table;は代入ではないのでconst char table[]と等価ではない、実際には*を使うことによってポインタとご認識をして操作するので異常終了していた、というだけの話でした。

ここでの教訓はいくつかあります
ひとつめ、JavaやC#に慣れすぎていた私はconst char[]をextern char*で参照したものがコンパイルエラーにもリンクエラーにもならなかったので、うまく連結していると思い込んでしまったのですが、CのLinkerはシンボルを名前で連結しているだけで型なんて見てくれていない、ということをすっかり忘れていました。
C怖いよC

もうひとつはexternはヘッダに定義して参照元と参照先が同じ型であることを証明しなければならないという基本を無視してはいけないということです。
このルールはMisra-Cでも規定されています。
今回の例でも、定義をヘッダにしていれば、コンパイラが文句を言ってくれました

//////data.h////////

extern const char* table;

//////data.c////////

#include "data.h"

const char table[1]={1};

//////logic.c////////

#include "data.h"

if(table[0]==1){

//////////////

この場合、data.hの定義ではdata.cのコンパイルが成功しません
当たり前ですね、型がちがうんですから

そして最後の3つ目、Cのexternはでたらめで怖いということ

やっぱり自分は高級言語の方が好きです

2015/08/15

[Android][Wear]リリースパッケージはモバイルとセットで

Android Wear対応のアプリをGoogle Playにリリースしたら、Googleからこんなメールが来ました。

////////////////////////

Hello,

Thanks for submitting your app for Android Wear. This is a notification that your app, Fairy in a watch, with package name com.nag.android.fairyinawatch, has not been accepted as an Android Wear app on Google Play.

Version Name: 1.0.0
Version code: 1
Production track: PRODUCTION

REASON(S) FOR EXCLUSION:


-Your app does not have Android Wear functionality that’s visible to the user.

If you’re adding wearable features to notifications, make sure that they are triggering when appropriate.

If you’re building a wearable app, there may have been issues packaging it. Be sure to:
- Include all the permissions declared in the manifest file of the wearable app in the manifest file of the mobile app. For example, if you specify the VIBRATE permission for the wearable app, you must also add that permission to the mobile app.
- Ensure that both the wearable and mobile APKs have the same package name and version number.
- Sign your app.
- Test your app on a variety of different Android Wear devices and configurations.

////////////////////////

私のアプリはWear上だけで動くものなので、Wearのapkをアップしていましたが、それを指摘されたようです。
エミュレータでは普通に動いていたのでなにも疑わなかったのですが、Wearアプリはモバイルアプリのセットとしてリリースしなければならないのですね。
Wearデバイスが直接Google Playに接続できない都合なのだと思います。
確かにWearはモバイルのサブ端末としてデザインされた側面が大きいとは思いますが、私のように単体で動くアプリだって用途にはあるはず、どんなものなんですかね。

仕方がないので、なにもしないモバイル用のアプリを作って、WearアプリをDependencyに加えてリリースパッケージを作りました。
実際にはAndroid Studioでプロジェクトを新規作成するときにPhoneとWearの両方にチェックを入れて作れば勝手に紐づけてくれるので何の手間もありませんでした。

私のアプリは何のPermissionも要求しないものでしたが、Permissionが必要なアプリの場合はダミーのモバイルアプリにも同じPermissionをつけなければならないようです。

またGoogleさんに怒られるかもしれませんが、無料でチェックしてくれてると思えば便利なものです。

2015/07/12

[Android]USBケーブルの不良

新しいPCにAndroid Studioをインストールして、開発作業を開始しようと思ったら不思議なところでハマりました。

端末を接続しているのに実行できません。
SDKのバージョンがインストールされていなかったり、端末のドライバがインストールされていなかったり、一渡りやらかしていたのはいたのですが、それでも実行まで進みません。

Androidは比較的親切にできていて、設定がうまくないときには何をしたら対応できるのかを親切に説明しているエラーが出るものなのですが、実行した時にエラーは出ていません。
ただただ端末でアプリが実行されません。"Uploading file"まで進むのですが、そこから進んでいないようです。
端末を接続するとOSが反応しますし、実行要求中にケーブルを抜くとエラーが出ますので、端末を認識していないわけでもないようです。

アプリが悪いのかなとシンプルなもの、実績のあるものでやってみたり、Rebuildを繰り返してみたり、Edit Configurationを確認したりしましたがどうしようもなし。


半日はまって途方に暮れたところで、何気なくUSBを別なスロットに差し替えたら一度だけ実行に成功しました。
もう一度トライしたらダメになっていました。
デスクトップPCだったので、背面のUSBポートに挿したらそこそこ安定して実行できるようになりました。

USBケーブルの不良でデバッグができなくなったのはもう何度目かになります。
ただ、以前はデバッグができなくなったときには端末の接続そのものができなくなっていたので、今回は大丈夫だと思い込んでいたのが失敗でした。

USBの規格の細かいところは存じませんが、私が体験した限り下の4つの状態があるようです。
1.正常に接続できて、デバッグできる
2.情報デバイスとしてPCと接続できているが、ADBでデバッグできない(ダウンロードが終わらない、今回の状況)
3.情報デバイスとして接続できていないが、充電はできている
4.充電さえできない。故障

安売りのケーブルだとすぐに3の状態になってしまうような気がしていますが、実際どうなんでしょうか。端末本体のプラグを痛めてしまって修理に出すことになってしまったり、USBケーブルには祟られています。
今回使っていたケーブルは再三のトラブルに懲りて結構お高いものを使っていたのですが、残念です。
どんなケーブルを買えばいいのか、またお店で悩んでしまいそうです。

2015/05/18

[Java][Android]なぜガーベージコレクションなのか


なぜ、Javaはガーベージコレクション(GC)を採用したのかおさらいしたいと思います。
ポインタを隠ぺいしたかった、スマートポインタを言語的に実現したかった、というのは的を得ていません。
それらはが逐次処理型のメモリマネージャでも実現可能です。

理由は至ってシンプルです。それはGCが”早い”からです。
GCの”遅さ”に辟易している方々には意外な話かもしれませんが、少し説明を聞いてください。

逐次処理の場合、メモリの解放が行われるタイミングで必ずその処理を実行しなければなりません。
処理をするということは、時間がかかるということです。
一方ガーベージコレクションを持っているメモリマネージャは参照カウンタを更新するだけで、メモリの解放という処理の本体を後回しにすることができます。
つまり、メモリの解放というアクションについては逐次処理より早いのです。
後回しにした処理は、CPUに余裕があるときに行えばユーザーにストレスを与えることはありません。

ガーベージコレクションはメモリとCPUのトレードオフにおいて、メモリ負荷を上げることでCPU負荷を減らす仕組みです。
メモリが潤沢にある環境ではとても効果的ですが、メモリにもCPUにも限界がある組込機器との相性は、いいとは言えません。
GCは時々、目に見えて遅くなります。時には未処理のメモリが詰まってOutOfMemoryExceptionを出すことさえあります。
どちらもメモリ限界を超えてしまったときに起こります。組込ソフトウェアの宿命なのです。

なんで逐次処理にしてくれなかったんだろうと恨み事の一つも言いたくなる気持ちは分からないでもありませんが、考えてください。
もし逐次処理だったらその遅さはアプリの動作全体にまんべんなくかかります。
最新のAndroid端末でさえ十分に快適な処理速度を持っているとは言えません。
大げさに言うともし、Androidがガーベージコレクションを持つJavaを選択していなかったら、今の普及はなかったのではないでしょうか。

AndroidアプリがOutOfMemoryに陥るのは組込ソフトウェアだからです。GCだからではありません。
メモリマネージャがいるからと言ってメモリのことを考えなくていいわけではありません。
GCにはよいところもたくさんあります。
逐次管理なら起こらないだろう不思議な現象に悩まされることもあると思いますが、GCと仲良くすることで、よりよいシステムを構築することもまたできると思います。
GCと正しく付き合っていれば、GCのせいで起こる問題っていうのは、実はそんなに多くないのではないかと思っています。
仲良くしましょう。

2015/05/17

[Java][Android]メモリリーク

最初に断っておかなければならないことがあります。
私は組込機器とJVMに若干見識があるものの、AndroidもそのほかのJavaも業務として扱った経験は0に等しいエンジニアです。
Androidを生業にされている方々には是非、実経験に基づいたご意見をいただきたいと思っています。

Androidの開発者さんの間でごく自然に「メモリリークが…」という会話をされているのを聞くたびに違和感を感じています。
Javaには理論的にメモリリークは発生しないはずです。言語上でメモリの生成消滅の管理を行わないからです。
実際にAndroidの開発をしていると簡単にOutOfMemoryExceptionに見舞われるのは知っています。
Java界面から見て既に不要になっているメモリがその原因になっているときに、それを既知のシステムにならって「メモリリーク」と言いたくなる気持ちは分からないではありません。
しかし、それは「リーク(漏れ)」ではないはずです。

ネイティブ言語においてのメモリリークは、確保したメモリのハンドルへの参照を失うことで、当該メモリを操作するすべを失うことを言います。

void test(){
 void*p = malloc(BUFFER_SIZE);

// free(p);
}

free();に引き渡されることなくスコープから外れてしまったpに格納されていたアドレスは、C言語の界面で拾うことはもうできません。
ハンドルを失ったメモリは、言語の界面から操作することが不可能になります。メモリープールからこぼれ落ちたメモリの断片をリーク(漏れ)と呼んだのはまさに言い得て妙です。

一方Javaはガーベージコレクタを備えたメモリ管理機能を持つ言語です。
Javaのメモリマネージャは参照を失ったメモリ(Javaの場合Object)を定期的に整理することができます。
こうしてJavaは常に必要なサイズのメモリだけを保持します。

OutOfMemoryが発生する理由はシンプルです。OutOfMemorey(メモリ不足)になったからです。
Androidは概ね組込機器上で動作しますから、メモリに限界はあります。使い過ぎればOntOfMemoryとなります。これは当然です。

問題はOutOfMemoryの原因を探っていった時に、その原因が既に不要になったObjectに起因していた場合、メモリリークと呼びたくなる気持ちは分からないでもありません。
例えば、Listに情報を詰め込むようなロジックで、設計者が意図しないデータが蓄積してOutOfMemoryが発生するものをメモリリークの例に挙げているページがありましたが、それはバグです、リークと呼ぶのはJVMに対しての言いがかりだと思います。

自分が意図しない参照がもとでメモリが過剰に消費されることはいくらもあると思います。
例えばstaticでないインナークラスが暗黙で持っている親クラスの参照について意識していない人はそこそこいるかもしれません。
(余談ですが、C#はインナークラスに親クラスのプライベートにアクセスする権限を与えましたが、親クラスのインスタンスは明示的に引き渡さなければならないように設計されています。この設計はJavaの失敗(?)を踏まえてか良く練らていると感心しました。)
シングルトンにするべくstaticに保存したインスタンスもそう簡単に消えません。
Viewサブクラスが便利だからという理由だけで上位クラスのViewを保存してしまうと簡単に循環参照になります。
それらは未熟な設計者の意図を超えた寿命を持ちますが、それは設計者の頭からリークしているだけで、システム的にはリークしていません。

とても大きなリソースを消費してしまうので、GCで処理されるのを待っていることができないクラスは存在します。
Bitmapは典型的な例です。
画像を展開するためメガ単位のバッファを使います。
参照が切れてもGCが走るまでバッファを維持してしまうのは、組込機器上許されることではありません。
ですので、Bitmapにはrecycle()というメソッドが用意されています。
不要になったBitmapは速やかにrecycle()をコールしなければなりません。
もうひとつの例はファイルアクセスです。
JavaはGCがあるから明示的にclose()しなくても大丈夫、と思っていると痛い目にあいます。
C++をバックグラウンドに持っているエンジニア-私もそうですが-は、スコープを外れた刹那に暗黙的に処理をしてくれるデストラクタに慣れているので、最初にJavaにデストラクタがないことに驚愕し、finalize()が同じように動いてくれないことに失望します。
CからC++にグレードアップしたときに縁が切れたとおもっていたclose()と再び付き合わなければならなくなって、がっかりしたものです。
正直、Javaの設計段階に参照カウンタが0になった時に呼び出されるメソッドがあっても良かったのではないかと思っていますが、後の祭りです。(C#はusingという仕組みを持っています。これもまたうまく作ったものだと感心しています。)
いずれにしても、GCを待たずに後処理を行わなければならないクラスは存在します。OutOfMemoryの原因にはなり得ますが、リークではありません。

循環参照等でガーベージコレクションの効率が著しく低下することはあると思います。
その場合、参照が切れてからもGCに掛かるまでにそれなりの寿命を持つことは想像に難くありません。
しかし、それはまだ相応の時間があれば処理できる可能性があるもので、リークではありません。
相応の時間が取れず、ギブアップするJVMの実装もないとは言えません。それは現実問題を解決しなければならない組込Javaとして妥当な判断であると思います。
循環参照は設計で避けることができます。(循環参照を病的に避けようとする、ある種のプロセスはまた有害だと思うのですが…)
万が一循環参照が解決できないメモリマネージャを持ったJVMがあるとしたら、それはリークではなくJVMのバグです。

OutOfMemoryになりやすい状況をメモリリークと呼ぶんだよ、と言われればそうなのかもしれません。
それならそれは、あまりいい傾向ではないと思っています。
上であげた例はほぼすべて、正しい知識、特にメモリマネジメントの知識があれば回避できる問題です。
もうひとつ、Androidエンジニアの方々には、失礼かもしれませんが、組込ソフトウェアを作っているという意識が不足しているように思えてなりません。
スマートフォンは組込機器なのです。限られたリソースとパフォーマンスの上で以下に効率よくサービスを構築するかというのが技術なのです。
それはCPUやローメモリが見えていなくても変わりはありません。
バーチャルマシン上で実行されるプログラムですので、実装依存の部分に深入りできないという事情もあり、そこの部分は1つのCPU上で動けばOKなネイティブ実装より問題は複雑です。
「リーク」という言葉を聞くと、その言葉に逃げているように思えてしまうのです。

最初の言葉に戻りますが、私はJavaをかじっている組込屋です。
Android専門家の方々の声をお聞かせくだされば幸いです。

2015/05/10

[Java]シャッフルの話

面白い例外を出したので、話のタネに。

配列をシャッフルする必要がありました。
Arrays.shuffle()という関数があればそれを使って終わりだったのですが、残念ながらはCollectionsshuffle()はあってもArrays.shuffle()はありません。シャッフルのためだけにわざわざコレクションにデータを移しかえるのは無駄なので(この認識は間違いです。正解はページの最後でw)別の方法を模索しました。
別の理由でこの配列をsortする必要がありましたので、おなじくsort()を使ってシャッフルができないかと思い付き、以下のようなComparatorを作ってみました。

class Comparison implements Comparator<Player> {
 @Override
 public int compare(Player lhs, Player rhs) {
  return rand.int(3)-1;
 }
}

compare()は2つのオブジェクトを比較して、大小の判定をします。
通常はデータを解析して適切な判断をするのですが、このComparatorは結果を乱数で返します。
比較結果がめちゃくちゃなのですから、まっとうに並ぶわけがないですが、シャッフルしたいのですから狙い通りです。
これは一見上手くいきました。
一見上品にもみえました。

しかし、このロジックは稀に例外を起こします。

java.lang.IllegalArgumentException: Comparison method violates its general contract!

general contract、一般契約に違反しています、というもの。
この場合の一般契約とは、Java.lang.Objectのメソッド、equalsが同じものは常に同じであることを保証するというものを指すのだと思います。
equalsとhashCoedeはObjectのメソッド、Java世界のすべてのオブジェクトが備えていなければならないものです。
例えばこれらはHashMap等にオブジェクトに格納する際に使用されます。
一般契約が守られなければ、これらJavaの根幹に当たる仕組みの動作が保証されなくなります。
とても大事なルールなのです。

さて、今回私はObjectをいじっているわけではありませんが、Objectが一般規約を守っているという前提で使用されているsortという仕組みを使っています。
どのようにして私のハッキングに気がついたのか定かではありませんが、恐らくはsortのロジックがあり得ない状態に陥ったのは想像に難くありません。
この仕掛けはやってはいけないものでした。

この話の怖いところは、上記の例外が以外と低確率でしかでないということです。
件のアプリでは、データ8つの抽選で遭遇することはまずなくて、80個程度の抽選で時々出るというくらいでした。
この致命的なバグは、試験が甘ければ気付かれることなく製品に入り込む可能性が十分にあります。

では、当初の命題、配列をシャッフルするにはどうするのが正しいのでしょうか。
今、私は以下のロジックを使っています。

Collections.shuffle(Arrays.asList(players));

別の記事で書きましたが、asListはplayersをラップする固定長のリストを返します。asListで返されたリストの内容を操作するとplayersの内容も操作されることが仕様上保証されています。シャッフルの場合ももちろんplayersのインスタンス内容が直接シャッフルされます。
リスト化する際にコピーが発生しませんから、配列をシャッフルするのとほぼ等価なパフォーマンスが期待できます。

ところで、こんな仕掛けを思いついてしまいました。

class Comparison implements Comparator<Player> {
 @Override
 public int compare(Player lhs, Player rhs) {
  if(lhs==rhs){
   return 0;
  }else{
   return rand.int(2)==0?1:-1;
  }
 }
}

同じインスタンスなら0(同じ)を返し、それ以外は0以外をランダムで返します。
比較は乱数ですが、equalsについての動作は保証されています。
まぁ、単なる興味で、使う気はありませんが。

[Android][Java]ArrayAdapterはアダプター

AndroidにはArrayAdapterというクラスがあります。
Adapterの実装の一つで、ListViewやSpinnerを使ったUIを実装する際には必須といえるクラスなので、触ったことがないという人は少ないと思います。
しかし、一方Adapterという非常に抽象的なネーミングだったり、文字列を表示するだけならカスタマイズの必要がなかったりして、サンプルコードをコピペしたまま良くわからずに使っているという人も多いのではないかと思います。
実は私もそうでした。
全容がわかっていなくても、表面上に出てくるところをいじっていると目前の目的が達成されるので、ついつい疑問を後回しにしていました。

このクラスを使っていると、不可解な現象に遭遇します。
前述のasListと同じく、追加ができる場合と例外が発生する場合があるのです。
個の現象について、仕様はなにも語っていません。
いろいろ整理していくと初期化の違いが影響しているらしいということが分かりました。

ArrayAdapterはいくつかのコンストラクタを持っています。

ArrayAdapter(Context context, int resource)// 1
ArrayAdapter(Context context, int resource, int textViewResourceId)// 2
ArrayAdapter(Context context, int resource, T[] objects)// 3
ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects)// 4
ArrayAdapter(Context context, int resource, List<T> objects)// 5
ArrayAdapter(Context context, int resource, int textViewResourceId, List<T> objects)// 6

1,2は要素を初期化しないコンストラクタ、3,4は初期値を配列、5,6はリストでそれぞれ提供するものです
下はそれぞれを動作確認するためのコードです。

private void test(Context context){
    String[]labels = {"test1", "test2", "test3"};
    ArrayList<String>array = new ArrayList<String>();
    for(String label : labels){
        array.add(label);
    }

    ArrayAdapter<String> adapter = ArrayAdapter<String>(context, 0);
    adapter.add("test4");//7

    adapter = ArrayAdapter<String>(context, 0, labels);
    adapter.add("test4");// 8

    adapter = ArrayAdapter<String>(context, 0, Arrays.asList(labels));
    adapter.add("test4");// 9

    adapter = ArrayAdapter<String>(context, 0, array);
    adapter.add("test4");// 10
}

このコードを実行すると7,10は成功し、8,9は例外が発生します。
この振る舞いは仕様に特に記述されていません。
この違いがなぜ起こるのか、しばらく疑問でした。

実は答えは簡単でした。
ArrayAdapterは外部にあるデータソースをGUIが操作できるインターフェイスに適合(Adapt)させるアダプターなのです。
私の誤認識はArrayAdapterが実装形式として内部に配列(Array)を持っているアダプタ(Adapter)だと思い込んでいたところにありました。
言い訳を許してもらえるなら、ArrayAdapterでなくCollectionAdapterだったなら、この誤解はなかったでしょう。
ArrayAdapterがアダプトするのはArrayだけではありません。ArrayListだけでもなく、すべてのコレクションを対象にしているのですから。

もうひとつ混乱を引き起こしたのは1,2のコンストラクタです。
こちらはデータソースのオリジナルがない状態を想定したコンストラクタだと思われます。つまり以下のように書くことができます。

ArrayAdapter<String>(this, 0, 0, new ArrayList<String>());

親切ではありますが、Adapterとしては過剰サービスだったのではないでしょうか。

ArrayListener.add()はデータソースのadd()をデリゲートしています。ArrayAdapterがデータの変更タイミングを察知するために必要なデリゲーションです。
こちらを通さない変更は都度notifyDataSetChanged()で知らせてやる必要があるのです。
構造が分かればいろいろなことがクリアになります。
実装ではありません、構造です。

2015/05/09

[Java]asListが返すリストは固定長

言語が進化していくにつれ、配列とコレクションの垣根は低くなっていきますが、依然としてそれは同じではありません。
さまざまな言語がさまざまなアプローチをしている分野で、この部分を俯瞰で見ていると、言語毎の思想が見えてきて面白いのです。

配列で保持しているデータをシャッフルして別なデータを生成するコードを書いていました。
具体的にはマッチメーカープログラムで、プレイヤーを一時的にシャッフルして対戦の組み合わせを決めるプログラムです。
プレイヤーのリストは順位で並んでいるので、これを壊したくはありません。
Javaに配列をシャッフル関数があれば良かったのですが、Arraysにはsortはあるもののshuffleはありません。
やむを得ずCollections.shuffleを使うことにしました。

Player[] generateShuffledArray(){
 List<Player>list = Arrays.asList(players);
 list.shuffle();
 return list.toArray();
}

直感的にasListは配列から新たにリストのインスタンスを作ると思っていましたから、シャッフルしたリストをさらにtoArrayで配列に戻して返しています。
効率の悪いロジックだなぁと思っていたのですが、実行してみると予想外の振る舞いになっていました。
この関数を実行した後、返り値の配列はともかく、オリジナルの配列playersまでがシャッフルされていたのです。
最初はどこかでgenerateShuffledArrayの結果を上書きしてしまったんだろうと思いましたが、その形跡はありません。
発想の転換が必要でしたが、listの変化がplayersに反映していると考えるしかありませんでした。
さて、仕様をみると、ちゃんと記述がありました。

As List
指定された配列に連動する固定サイズのリストを返します。返されたリストへの変更は、そのまま配列に書き込まれます。このメソッドは、Collection.toArray() と組み合わせることで、配列ベースの API とコレクションベースの API の橋渡し役として機能します。また、返されるリストは直列化可能で、RandomAccess を実装します。 

実装についての記述はありませんが、Arrays.asListから返ってくるListは公開されているどんなListサブクラスとも異なる、生の配列をラップする実装となっているクラスであると考えるのが妥当です。
(手元で確認したところ、クラス名はArrays$ArrayListとなっていましたので、ここではその名前を使います。名前は実装依存です。)
固定長であるのは、サイズ拡張によって与えられた配列と別インスタンスにならないための制限でしょう。

通常、実装に依存するようなデザインはぜい弱で、美しくありません。
ネット上にはこのデザインについて、”初期実装のバグを引き継いだ”ような説を唱えている方もいるようですが、これには理由があり、Javaという言語を理解する上で大事なポイントだと思っています。

開発初期のJavaは今よりはるかにパフォーマンスについての要求が厳しいものでした。
ネイティブコードを生成するCやFortranでさえ満足できる処理速度が出せず、クリティカルな個所はアセンブラで書くというのがまだ選択肢にあった時代です。
バーチャルマシン上で動作するというコンセプトのJavaはおもちゃ扱いされていました。
配列の処理は実装効率をてきめんに反映します。
データを物理的にフラットにバイト単位で並べてオフセット値でアクセスする配列は、ある意味抽象化からもっとも遠い存在です。
Javaが仮装言語であることに固執したなら、配列そのものを否定する選択肢もあったはずです。
その場合、配列は必ずコレクションの内部形式にコピーされますから、パフォーマンスは目に見えて落ちます。
理想と現実のせめぎあいの中で、配列をそのままラップするArrays$ArrayListという発明は絶妙だと思います。
実行効率をほぼ落とすことなくリストとしての機能を提供することができるからです。
Arraysをビルダーにしたデザインも賢明でした。実装に強依存するクラスがオーバーライドされて拡散するのとを防いでいます。
Javaにはコレクションを初期化する際にコレクションを引数に取るコンストラクタはありますが、配列を引数にとるコンストラクタはありません。恐らくasListのデザインとひとつながりなのでしょう。初期化の違いで動作が変わるか、実行効率を最適化するチャンスを逃すか、究極の選択をしないという判断です。
実にうまくできています。

さて、当初の想定通り、オリジナルをシャッフルしないなら以下のように修正します。


Player[] generateShuffledArray(){

 List<Player>
 list = Arrays.asList(players.clone());
 list.shuffle();
 return list.toArray();

}

オリジナルがシャッフルされることを容認するなら実装はこれだけです。もうメソッドにする必要すらありません。

void generateShuffledArray(){
 Arrays.asList(players).shuffle();
}
どちらのコードもパフォーマンスは悪くない筈です。少なくとも言語的にはそういう実装ができるデザインになっています。

2015/04/17

[Android]Serializableの限界

Androidでシリアライズを用いると、割と簡単にStackOverflowExceptionを出すことができます。

私が直面したのはマッチメーキングをするアプリの作成中でした。
プレイヤーとプレイヤーを組み合わせた対戦履歴のリストがあり、対戦はプレイヤーのペアを保持し、プレイヤーは自分の対戦を保持します。
リレーショナルデータベースではありがちな構造だと思います。

ObjectOutputStream.writeObjectの実装は対戦履歴を読みに行ってプレイヤーを見つけ、プレイヤーの持っている対戦履歴を見つけ、さらにその中のプレイヤーを読んで…と走査していくはずです。
なんのチェックもなければ無限ループに嵌ってスタックオーバーフローを起こすでしょう。
最初はこの無限ループに嵌ったのだと思っていました。
しかし、小さいサンプルや少ない組み合わせだとループを作ったつもりでもシリアライズが成功するので、理屈がわからずにしばらく悩んでしまいました。
おそらく、走査したオブジェクトのリストを持っていて、一度走査したオブジェクトを発見した時点で走査を切り上げる、という実装になっていると思われます。
それでも結局スタックオーバーフローに陥ってしまうのだと思われます。しかも、それほど高負荷をかけなくても。
スタックをどの位確保しているかは実装依存だと思いますが、私の手元のGalaxy S2 Android 4.0.2の場合、8人の対戦を2回戦するとスタックオーバーフローが発生します。
これは実用上無視できる規模ではもちろんありません。

本物のコードは少々複雑なので、問題を簡易化したサンプルを作って検証しました。
AとB、2つのクラスがそれぞれのインスタンスを保持しあう構造で、どこまでシリアライズが可能かを確認します。

class A implements Serializable{
 B b;
}
class B implements Serializable {
 A a;
}

A instance;

for(int i=0; i<128; ++i){
 A a = new A();
 instance = a;
 for (int j = 0; j < i; ++j) {
  a.b = new B();
  a.b.a = new A();
  a = a.b.a;
 }
}

ObjectOutputStream o = new ObjectOutputStream(openFileOutput("test",0));
o.writeObject(instance);
o.close();


これもGalaxy S2 Android 4.0.2の場合、i=15でスタックオーバーフローが発生します。

解決編に進む前に、もうひとつ面白い挙動を見つけたので、そちらも紹介しておきます。

件のマッチメーカーアプリは回転に対応しています。
回転した際にActivityが再構成されますから、データを引き渡さなければなりません。
アプリはプレイヤーのリストと対戦履歴のリストをどちらも保持するゲームのオブジェクトを持っていて引き渡します。
onSaveInstanceStateで保存して、onCreateで再生するという、一般的な処理です。

オブジェクトを引き渡すのに使うメソッドputSerializable()、引数はObjectではなくSerializableです。
なので私はすっかり旧Activityから新Activityにデータが引き渡される際にデータはシリアライズ>デシリアライズが行われていると思い込んでいました。
しかし、だとしたら、回転時にStackOverflowExceptionが起こってしかるべきです。
実際、例外は発生せず、データを引き継いでの回転が行われていました。(事実は、先に回転の実装を行い、実績があるので保存もシリアライズを使おう、と思って嵌ったのですが。)
ストリームへのシリアライズとActivity間のシリアライズにどんな違いがあるのか、やはりしばし悩んでしまいました。

デバッガで確認したところなんのことはない、回転後のゲームオブジェクトは回転前と同じidを持っていました。
シリアライズなどされずにインスタンスそのものが引き渡されていたのです。

さてこれらの実験からオブジェクトのシリアライズは小さい単位でやろうねとか、回転したときには同じインスタンスが渡されるよとか結論するのは絶対ダメです。
シリアライズで無限ループが起きていないのも、同一インスタンスが送られているのも、実装依存です。
スペックに実装を規定するような記述はありませんでした。
他の端末では無限ループになってしまうような実装がされているかもしれません。
他の端末では毎回まじめにシリアライズが行われているかもしれません。
タイミング依存で、我がGalaxy S2でもシリアライズをすることがあるかもしれません。
例えばActivityが隠されていて、結構な時間がたった後に再表示される場合とか、ありそうな話です。
上品プログラマーはスペックに記述されていないいかなる挙動も想定してはいけないのです。

結論です。
Android Javaにおいては、相互参照するオブジェクトをシリアライズしてはいけない、と考えるべきでしょう。
データはデータベースで管理して、JavaオブジェクトはIDだけを持っていなさい、というのが一番Android向きの設計なのだと思います。が、それは言わないお約束にして、私はこんな感じで解決してみました。

class A implements Serializable{
 transient B b;
 int id;
 void setB(B b){
  this.b = b;
  this.id = b.id;
 }
 
 boolean bind(B[] bs){
  for(B b : bs){
   if(b.id == id){
    this.b = b;
    return true;
   }
  }
  return false;
 }
}

class B implements Serializable {
 int id;
 A a;
 B(int id){
  this.id = id;// it must be unique
 }
}
クラスAは保存用のidとBの参照を持つというハイブリッド構造です。

Bの参照がtransient宣言されているので保存されない、というのがミソです。

これで必要以上の循環参照は発生しません。要は参照構造が途切れていればいいので、Bに仕掛けは入れません。



この仕組みだと、デシリアライズしたときtransient宣言された参照bに値は入っていませんから、bind()でidをもとにデータバインディングをやり直さなければなりません。



余談になりますが、C#には何種類ものシリアライザが用意されています。

代表的なもののうち、BinaryFormatterは相互参照しているオブジェクト群をシリアライズすることができます。Windows OSで動くことが事実上決まっているので、上記のStackOverflowのようなことは起きないと思われ、使用するのは安全だと思っています。

もう一方のXmlObjectSerializerは相互参照しているオブジェクトのシリアライズを試みるとエラーになります。不親切にも見えますが、自分にできないことは受け入れないというのは正しい振る舞いです。

Javaはさまざまなプラットフォームで動かなければいけない宿命を持っているので見極めが難しいのですが、今の振る舞いならXmlObjectSerializerと同じく最初からNotSupportedExceptionを出してくれる方がむしろ親切なのではないかと思いました。





2015/04/12

[Eclipse][Android Studio][Android]EclipseとAndroid Studioの比較

星取表を作って優劣を見える化するような情報を求めている方には申し訳ありません。
もっとぼやーっとした一般論です。

そもそも、Android StudioはAndroid専用のツールのようなので、私のように他のプラットフォーム用のJavaも書くエンジニアに強い魅力はありません。
Android Studioを導入したからと言ってEclipseを削除することはありません。
そういう意味では、Android Studioにちょっと不利なレビューになっているかもしれません。

EclipseもAndroid Studioもカスタマイズし放題のIDEなので、カリカリにチューンしてしまったらどっちがいいもないでしょう。なので、比較はデフォルト動作で行っていると思ってください。
上品プログラマとしてはよほどのこだわりがない限りそのツールのデフォルト設定で使うように心がけています
機種変更したり、他人のPCで作業したりしたときに困らないようにと身に着けた習慣です。
ひとり1ノートPCが当たり前の今では無用なこだわりかもしれませんが、私がこの仕事を始めたころはコード書くのは共有PCなんて時代だったのです。
また、デフォルト動作がそのツールの設計思想に一番馴染んでいて、設計者が一番使ってほしいスタイルに違いない、という思想もあります。

と、前ふりをしてなんですが、EclipseとAndroid Studioはすごく近い思想でできたツールのようで、見た目のデザインの違いを乗り越えると使い勝手はそんなに変わらないように思います。

一番ドラスティックな違いはリソースファイルの位置ですか。
Eclipseはプロジェクトのルートにsrcとresが並んでいましたが、Android Studioはsrcの下に階層が掘られて、ソースコードが格納されたjavaとresが並んでいます
この違いのため、残念ですがEclipseとAndroidで環境を共有することはできません。
リンクを作り直すのも面倒なので、GitHubなどのリポジトリは作り直しました。

比較するとEclipseはシンプルにわかりやすく、余計なことは極力行わないツール、Android Studioは効率の良い開発をするためにいろいろ気を回してくれるツールのようです。
Android Studioの気配が気に入るか、過剰に思うかで好みが分かれるのかなと思います。
無名クラスをラムダ式にたたんでくれるのはすごいなと思いましたが、気が付くまで気持ちが悪かったです。
私はC#もやるので気持ち悪いなぁと思いながら見過ごしていたのですが、Javaしか知らない人はびっくりするんじゃないんでしょうか。
リソースidを文字で置き換えて見せてくれるのも、便利なんだと思いますが、最初は気持ちが悪かったです。
どちらも私は、慣れれば使えそうなのでもうしばらく使ってみますが、気に食わなければ設定で無効にすることはできるでしょう、きっと。

Eclipseだとエラー行にカーソルを置くことで表示された修正候補はALT+Enterで起動します。
Enterをたたくのは結構勇気がいるので最初は戸惑いがありましたが、慣れると快適です。
予測変換について、Eclipseは一文字でも間違えるとギブアップしていたのですが、Android Studioは訂正すると付いてくる根性があります。

Eclipseはいい意味でも悪い意味でもやっていることが推論しやすくて気持ち悪さのない”ツール”でしたが、Android Studioは開発効率を上げるために様々な工夫が入っているツールなのかなと感じています。
24時間でアプリを組み上げる競技プログラマさんなんかはAndroid Studioに魅力を感じるのかもしれません。
上品プログラマとしては質素なEclipseが大好きなんですが、Android Studioに任せて最少手順でコードを書くという経験も捨てがたく、甲乙つけ難いところです。
公式ページがEclipseを削除してしまったので、新規の方がわざわざEclipseを使う必要はないと思いますが、Eclipseに馴染んでいる人が敢えて引っ越さなければならないモチベーションも今のところ感じられない、というのが2015年4月時点での結論となるでしょうか。

考えてみれば当たり前のことですが、EclipseもAndroid Studioも目的はAndroid用のコード生成なので、本質的にやることは変わりません。
昔は同じC++でもMFCとBoland Cでニュアンスが違っていろいろややこしかったりしたこともありましたが、そういうことはありません。
同じDalvik向けのクラスファイルを吐くエンジンを使っている2ツールに本質的な違いはないのです。
Android開発作業の中でツールに依存する部分というのは何%もないでしょう。そういう意味ではどちらを使うかというのは実に些細なお話だと思います。
ツールとしてEclipseを前提にしている情報がAndroid Studioでまったく使えなくなることはまずないでしょう。
所詮がところ同じコンパイラでDalvik向けのクラスファイルを吐く仕組みですから、言語としては同じものです。
すでにEclipseで開発している人がAndroid Studioに切り替えたからと言って、今までの知識が一夜にして無駄になることもありません。

結論、見るまえに跳べ。

[Android Studio][Android]Android Studioに引っ越す

牛にひかれて善光寺参り。
しばらくEclipseを使っていこうと思っていたのですが、PCが水没してしまいました。
作業環境を再構築しなければならなくなったのを機会にAndroid Studioを使ってみることにしました。

インストールするものは、公式ページからダウンロードしました。

https://developer.android.com/sdk/index.html

去年まではここからEclipseがダウンロードできたのに、前バージョンへのリンクすらありません。
前の記事でEclipseを推していた私ですが、あっという間にリンク先が切り替わってしまい、ご迷惑をかけた方がいらしたら申し訳なかったです。
なにがあったのか背景をご存知の方、教えてほしいです。

私の場合は前述のとおりPCクラッシュからのやり直しだったので、Javaのインストールから行いました。
Eclipse環境がある方はAndroid Studioのインストールをするだけで済むはずです。
ダウンロードしてインストーラに従ってボタンを押すだけでした。
Android ADTがパックになっていなかった時代のEclipse構築を思い返すと本当に簡単になりました。

続けて日本語化というステップを当然のように記述されているサイトも見受けられますが、上品プログラマーとしては英語のままで使われることをお勧めします。
開発ツールは最低限単語さえわかれば使えます。最低限の単語がわからないようならそもそもソフトウェアの開発に困るはずです。わかるくらいまでは勉強しましょう。
日本語化して余計なトラブルを呼び込むことはままありますし、日本語だけで仕事ができる時代じゃないんですよ、ほんとうに。

続けてEclipseのプロジェクトをAndroid Studioに変換します。
Android StudioのImportはEclipseをダイレクトに読み込めるようになっていますから、なんの心配もありませんでした。
ディレクトリ構造が変わり、Gradleなる意味不明のものががつっと加わりますが、そこらへんは当面お任せしておけばよい感じでした。
Gradleについてはいずれ知らなければならないもののような気がしていますが。

インポートできたので、ビルドしてみます。
私はここで軽くひっかかりました。
ここまでダウンロードの待ち時間を含めても30分程度で来てしまいましたし、まぁ、少しくらいはなにかありますよね。

エラーコードを読んでみると、くだんのGradleさんがお怒りになっている様子でした。正直少しやばいぁなと思いました。
エラーコードをそのまま検索エンジンにコピペすると、Android Studioのスタートアップに困っている人のためのページがいくつもヒットしました。
同じ思いをしている人って結構いるんだなぁと思うと少し気持ちが楽になりました。

残念ながらそのものずばりの解決策は見当たらなかったのですが、Gradle Scriptに問題があるらしいなと目星をつけてよく見直してみると、じつはなんのことはない、Android Studioがエラーの起こっている行を赤いアンダーラインで表示してくれていました。
私の場合はAndroidのライブラリが2つ宣言されていて、ダブってるよ?というものだったので、片方をコメントアウトすることで無事ビルドが通るようになりました。

build.gradle(Module:app)
dependencies {

//    compile 'com.android.support:support-v4:20.0.0'
    compile files('libs/android-support-v13.jar')
}

オリジナルのプロジェクトのターゲット設定に手を加えていたので、そのせいでAndroid Studioが誤認識したのだと思います。
わかってしまえば単純明快です。
私の場合、前の環境をロストしてしまっていたのでやりませんでしたが、Eclipse側のお作法を直してインポートしなおすという手もあるのかな、と思いました。
いずれにしてもいえることは、お作法通りに作っていればツールは何とかしてくれるものだということ。大抵の場合問題はコーダー側にあります。

別のプロジェクトでも別のエラーが出ました。
今度は冷静に情報を観察することができました。
今度もライブラリファイルに問題があるようでしたが、上の例のように2つ宣言されているというものではありませんでした。
問題の行のフローティングメッセージを見ると、バージョンが"20"では低すぎるようなことが書いていたので、21に書き換えたところ、ビルドできるようになりました。
素直にエラーコードとエラーメッセージを読めば対応できるみたいです。

私の感覚では、EclipseからAndroid Studioへの引っ越しは”簡単”でした。

2015/03/09

[Java][Android]CharSequenceの話

例えばAlertDialogクラスのメソッドsetTitleは引数を1つ持っています。与えるべきなのはダイアログのタイトル部分に表示する文字列ですから型はStringかと思いきやCharSequenceというインターフェイスです。
よく見るとAndroidのクラスでStringを引数にするものは皆無といっていいのですが、普段意識することはあまりないと思います。
StringはCharSequenceを実装していますから、CharSequenceで宣言されている引数にはなんの問題もなくStringを使うことができます。なので意識していない人もすくなくないのではないかと思っています。
実際、J2EEで同じ役割を果たしているawtクラスの引数はStringです。普通のコーディングをしている方々ならStringで受けてくれた方がすっきりするのではないでしょうか。
(ちなみにCharSequenceは導入バージョンはJava1.4です。1.0から存在しているawtに採用されていないのは順番的に仕方ないと言えば仕方ないのですが…)

さて、CharSequenceはこんなインターフェイスです。

---

abstract charcharAt(int index)
Returns the character at index.
abstract intlength()
Returns the number of characters in this sequence.
abstract CharSequencesubSequence(int start, int end)
Returns a CharSequence from the start index (inclusive) to the end index (exclusive) of this sequence.
abstract StringtoString()
Returns a string with the same characters in the same order as in this sequence.

上から文字を取り出す、長さを求める、部分文字列を切り出す、そして文字列化する、というStringの基礎が明示されているだけです。
確かに表示するだけの文字列として最小限の命令セットにはなっています。
インターフェイス化はオブジェクト指向の基礎ですが、それだけではまだ、わざわざインターフェイスにする理由としては弱いと思っていました。
とはいえ現実問題インターフェイス化にはそれ相応のコストがかかります。JavaのStringはJava黎明期からコストパフォーマンスに非常に注意して作られたクラスです。普通に考えればダイレクトに使った方が実行効率はよいはずなのです。

StringサブクラスはUIを作っていればあちこちで使いたくなります。たった4つ、いや、toString()はObjectが持っていますから最低限3つの実装ではありますが、いちいち実装するのは上品ではありません。
なんでStringにしてくれなかったんだろう。そうしたらStringを継承するだけで済んだのに…

と思ってやっと気がつきました。理由は多分Stringがfinalだからです。
手前みそながら、前述のLabeledItemを設計した時です。

以下のコードはコンパイルできません。Stringがfinal宣言されているからです。

class SomeString extends String{

引数をinterfaceのCharSequenceにしておくことによって、機能拡張したStringぽいオブジェクトを引数に渡すチャンスを残した、と考えるべきでしょう。
おかげで私はLabeledItemを実装することができました。
awtは…多分、やらかしちゃったんでしょうね。
私はawtでがりがりUIを作った経験がないのでわかりませんが、黎明期のJavaコーダーさんのご意見を伺ってみたいものです。

ところでJavaには、CharSequenceを追加する他にもう一つの選択肢があったと思っています。
それは引数をObjectにすることです。
ObjectはtoString()を実装しています。つまりすべてのオブジェクトは文字として表すことができるように設計されています。個人的にはJavaの大変優れたデザインだと思っています。
Objectにしなかった理由は-これは本当に推測の域をでないのですが-JavaにおいてtoString()はデバッグ用という意思があったからではないかと思っています。
私はtoString()というデザインが好きな関係上、コードの中にUI上に表示するものとしてtoString()をオーバーライドすることがよくあるのですが、このスタイルはJavaとしてはアウトなのかもしれません。

さて、さらっと流しましたが、実はもうひとつの疑問がありました。
なんでStringはfinalなんでしょうか?
その話はこの次に。

2015/02/28

[Android]LabeledItem:RadioButtonを簡単に使うためのクラス


Stringについてのうんちくを使用と思ったのですが、書き始めるといろいろ裏を取らなければならないことがあり、そのためにはJavaの言語仕様やバーチャルマシン、Java Byte Codeの仕様まで読み込まなければならないと気が付いて気持ちが萎えています。
ネットに情報を上げている方々ってすごいですね、やっぱり。
一日考えたらいろいろ思いついたので追記しています。
ネットで発言はやっぱり難しい。

前に継承できるStringクラス、AbstractCharSequenceを紹介しました。

実は私は最終的にこのクラスを使用していません。
そのあとすぐに、もう少し便利で潰しが利くクラスを思いついたからです。

public class LabeledItem<T> implements CharSequence{
 private String label;
 private T value;

 public LabeledItem(String label, T value){
  this.label = label;
  this.value = value;
 }

 public T getValue(){
  return value;
 }

 @Override
 public String toString(){
  return label;
 }

 @Override
 public char charAt(int index) {
  return label.charAt(index);
 }

 @Override
 public int length() {
  return label.length();
 }

 @Override
 public CharSequence subSequence(int start, int end) {
  return label.subSequence(start, end);
 }

 public static int indexOf(LabeledItem<?>[]items, Object value){
  for(int i=0; i<items.length; ++i){
   if(value.equals(items[i].getValue())){
    return i;
   }
  }
  return -1;
 }
}

ジェネリッククラスになっていますので、データ型は自由に定義できます。こうするとAbstractCharSequenceを継承せずとも十分拡張性があるので、直接実装しています。
indexOfの第一引数はLabeledItemにしたかったのですが、文法エラーになります。
Tはインスタンスにかかる形容詞なのでstaticメソッドには使えません、とのこと。
型は指定しないがなんらかジェネリッククラス、という意味のにしてエラーは消えました。

ところで、LabeledItemをsetSingleChoiceItemsに適用するにはまだひと手間かかります。
第一引数に与えるためにLabeldItemの配列を作らなければならないのですが、素直に作った下記のクラスはコンパイルエラーとなります。

LabeledItem<Integer>[]items = new LabeledItem<Integer>items(){
 new LabeledItem<Integer>("item1",123),


ジェネリッククラスの配列は作ってはいけない模様。
釈然としないのですが、今は受け入れて回避策を練ってみました。

回避策1:CharSequenceの配列とする

CharSequence[]items = new LabeledItem<Integer>items(){
 new LabeledItem<Integer>("item1",123),

setSingleChoiceItemsの第一引数はCharSequence[]ですから、これで十分です。問題は選択された後、CharSequenceのインスタンスをLabeledItemにキャストしてあげる必要があり、ちょっとカッコ悪く、しかもワーニングになります。

回避策2:LabeledItemの配列とする
LabeledItem<?>[]items = new LabeledItem<Integer>items(){
 new LabeledItem<Integer>("item1",123),

型を不定にすると配列が作れるようになる、というのはやはり釈然としません。ここらへんの仕組みが血肉になっていないからなんだと思います。これもやはり選択された後にキャストが必要です。
なんとなく仕組みがわかってきました。そのうち解説しますが、このやり方はジェネリック文法の穴を突いた感じなのでよろしくないですね。

回避策3:LabeledItem<>を継承したクラスを作る
public class LabeledIntItem extends LabeledItem<Integer> {
 public LabeledIntItem(String label, Integer value) {
  super(label, value);
 }
}

LabeledIInttem[]items = new LabeledIntItem[](){
 new LabeledItem<Integer>("item1",123),


ただ1枚クラスを被せるだけでコンパイルを通ってしまうのもまた不思議です。これならキャストなしで使用できますが、せっかく流用できるように作ったジェネリッククラスをデコレートするクラスを作るというのが本末転倒です。
なんか違う方向に努力しているような気はしています。

回避策4:Object型の配列に格納する。

Object[]items = new LabeledItem<Integer>items(){
 new LabeledItem<Integer>("item1",123),

そもそもJavaの原始デザインにジェネリック型はありませんでした。コンテナクラスにはObject型で格納する、取り出す時にキャストするというのがルールだったのを思い出しました。
回避策1、2のようにワーニングすら出ませんからいっそすがすがしいと思います。

回避策5:コンテナクラスを使用する

  final List<LabeledItem<Integer>> items= new ArrayList<LabeledItem<Integer>>(){{
    new LabeledItem<Integer>("Zero", 0);
    new LabeledItem<Integer>("One", 1);
  }};

  .setSingleChoiceItems(items.toArray(new CharSequence[0]), 0, new OnClickListener(){

いろいろ考えた挙句、現在のJavaではこれが正解なのかな、と行きついた形です。
ジェネリッククラスで配列を作ることはできませんが、コンテナクラスを作ることはできます。これはCから受け継いだ配列という概念の限界を示しているのかもしれません。
上のように宣言すればコード上の扱いやすさは配列と大差ありません。
ポイントは、CharSequenceで扱われるときには正しくCharSequenceの配列として使用するというところでしょう。ここでジェネリッククラスの配列にしようとすると無限ループにはまります。

突っ込み歓迎します。

2015/02/22

[Java]extendsできるStringを作る

Stringがfinalで宣言されていて困ったので、自前で作ってみました。

最初に断っておきますが、Stringがfinalになっているのには相応の意味がありますので、理解してから、適切な使い方をしてください。

AndroidのAlertDialogで選択するアイテムを上品に作ろうとしていました。
setSingleChoiceItemsの第一引数は一覧に表示する文字列の配列です。普通はここにStringの配列を渡しますね。
私はここに選択するべきデータを与えたいと思いました。ラベルとデータで2つの配列を持つのは恰好わるいじゃないですか。

データのtoStringを目的に合わせて実装しておけばいいよね、と思っていたのですが、そう簡単にはいきませんでした。
データがCharSeqece(なんでStringじゃないの?の謎はまたあとで)を継承していないので適用できませんとのこと。toStringを持ってるんだからObjectでいいじゃん、とも思ったのですがこれもまた掘り下げると長くなりそうなのであとで。

それならStringを継承すればいいよね、と思ったら今度はStringがfinal宣言されているのでextendsできませんということ。これもまた理由があるのですが、長くなるのであとで。

素直にインターフェイスCharSequenceを実装すればいいんでしょうが、CharSequenceは複数のメソッドを持っていて実装はそこそこ面倒です。私の場合、同じ目的でいくつかのメニュにこの仕掛けを適用しようとしていましたので、データ毎にCharSequence実装するのは上品ではないなぁと思わざるを得ませんでした。

そこで、発想を転換して作ったのがこのAbstractCharSequenceです。

public class AbstractCharSequence implements CharSequence{
 @Override
 public char charAt(int index) {
  return toString().charAt(index);
 }

 @Override
 public int length() {
  return toString().length();
 }

 @Override
 public CharSequence subSequence(int start, int end) {
  return toString().subSequence(start, end);
 }
}

AbstractCharSequenceはGoFデザインパターンでいうところの典型的なAdapterです。
AbstractCharSequenceはtoString()で返される文字列を元に、CharSequenceを実装するという実に本末転倒なクラスです
表示に使う文字列はtoString()をオーバーライドして与えます。
toStringはデバッグ用、というのがJavaの基本概念のような気もしてます。このやり方は邪道かもしれません。参考まで。

public class AValue : extends AbstractCharSequence{

 private String label;
 private String value;

 public AValue(String label, String value){
  this.label = label;
  this.value = value;
 }

 @Override
 public String toString(){
  return label;
 }

 public String getValue(){
  return value;
 }
}

AbstractCharSequenceを実装したクラスをsetSingleChoiceItemsに与えることによって、データそのものを表示するインスタンスにできます。

最初にも書きましたが、この手法はわりとJavaという言語の設計思想の根幹に踏み込んだことをしています。今回のような明確な目的を持たずに乱用するとよくないことがおこるかもしれません。
少なくとも、AbstractCharSequenceは実体を持ったクラスなので、真にObjectmもしくはStringから継承されるべきクラスにのみ適用すべきです。ほかにも継承する候補があるクラスはそちらを優先すべきです。対応は簡単で、CharSequenceを継承、実装してください。

2015/02/21

[Android]有料版と無料版の切り替え

[2015/06/01追記]Android Studioでは下記手順のほとんどを無視して簡単にパッケージの切り替えができることがわかりました。また、やり方についてもベターな方法を編み出しました。Eclipseユーザー向けにこのページを残しておきますが、Android Studioユーザーは新しい方法を推奨します。

Google Playにアプリを公開するとき、有料版と無料版は別のアプリとして登録しなければなりません。
広告が入るくらいしか違いがないのだから簡単に切り替えたいですが、パッケージ名を変えるといろいろ祟りがあってイヤですよね。
Android開発し始めたばかりでまだツールの使い方や、AndroidManufestのいじり方もわからなかったころは本当に恐怖でした。

いくつかアプリを作ってみて、なんとなくコツが掴めてきたのでまとめておきます。

まず最初に、Eclipseはパッケージの切り替えについて効率の良いツールを提供してはいない、というのが私の結論です。上品プログラマーとしては、用意されたツールを使わない手はないとおもってあれこれ調べたのですが、あきらめざるを得ませんでした。
考えてみれば、パッケージはアプリの名前みたいなものですから、さくさく切り替えられてはたまらない、というのは理解できます。

AndroidManufest.xmlにかかれてるpackageを書き換えるとソースコード上に一気にコンパイルエラーが発生しますが、これはリソースIDを管理しているgen以下のパッケージ名がpackageに依存しているからです。
リソースIDを管理している<パッケージ名>+".R"というファイルのファイル名が変わるので、コード内のimport文をすべて書き換えるのは必須作業です。
逆に言うと、手間がかかる作業はこれだけで、あとは合理的に扱うことができます。

余談ですが、AndroidManufest.xmlに記述されるパッケージ名とアプリで使われているパッケージ名が同じである必要はないようです。わざわざ変える必要もないのですが、二つの間に関連性がないと気が付かないで嵌ったことがあるので、念のために。

以下は私がやっている手順です。

  1. 無料版用のパッケージを作ります。起動Activity、EclipseのウイザードがMainActivity.javaという名前で作るあれの格納されたパッケージと同列に”XXX_free”という名前で作っています。
  2. 新しく作ったパッケージに無料版に必要なクラスを移動します。上品プログラマーなら無料版と有料版のクラスを切り分けるためのデザインはできているはずです。最低限、起動Activty、例ではMainActivity.javaだけは複製する必要があります。起動時に有料版と無料版を切り替えるからです。単純に広告が入っているだけの違いなら起動Activityの複製だけで済むはずです。
  3. AndroidManufest.xmlの内容を書き換えます。
    - ?xmlタグのpackageをfree版のパッケージ名に切り替えます。例だとXXX_hureeです
    - 起動Activityをフリー版に切り替えます。intent-filterでが指定されているActivityです。これで無料版の起動Activityが呼ばれるようになります。
    - 無料版で必要なPermisionを追加します。広告を入れるなら、ネットワークなどの権限を足さなければなりません。逆に不要なPermissionは削除します。不要なPermissionが残っているアプリはセキュアではありませんし、マルウェアと思われかねません。PermissionはProguardも落としてくれません。
    - バージョン番号の更新が必要なら更新しておきましょう。上品プログラマーはバージョン番号を大事にします。
  4. String.xmlのapp_nameを無料版に書き換えます。多言語対応している場合はすべての言語で更新するのを忘れずに。
  5. Cleanしてからフルビルドします。このときアプリによっては大量のエラーが出ます。エラーの原因は自動生成されたクラスのパッケージ名が変わったからだけですから、あわてずにimport文の.Rで終わる一文を新しいパッケージ名に書き換えます。
  6. proguardをかけます。不要なクラスやライブラリはproguardが落としてくれるので、気にする必要はありません。proguardの設定は有料版と無料版の共用で構いません。
以上が私のやっている方法です。
一気に書き換えるツールを作ったら需要ありますかね。5分もかからないので手作業でやっていますが。


2015/01/14

[Java]Eclipseが起動しない

アプリネタを思いついたので久しぶりにEclipseを立ち上げたら起動しなくなっていました。
アイコンをクリックすると、少し間をあけてだらだらとエラーメッセージらしき文言が並んだダイアログが開くだけです。
なんとなく、Eclipse.iniの内容に似ているような気がします。
全部読んでも実がないので、検索してヒットしそうな文言を探してみました。

Java was started but returned exit code=13

その下はjavaw.exeまでのパスです。どうやらjavaw.exeがエラーで止まっている模様です。

面倒臭いけど再インストールすればなおるんじゃね?と思ってオフィシャルページに行ったら、Eclipse+ADTがなくなってAndroid Studioなんてものになってるじゃないですか。
これは焦りました。
あまりに焦って丸一日放置。

乗り換えのチャンス、と言えなくもないですが、過去の経験上こういう時、新しいことをしようと思うとはまっていくので、ぐっとこらえて現状解決を優先することにしました。

やり直せばなんとかなるんじゃね?と思ってeclipseを再インストール(というか、オリジナルを解凍して置き換え)してみたのですが、現象は変わりません。
Java側だと面倒くさくていやだなぁ

ググってみるといくつか同じ現象の報告がありました。
こちらのページが最も理路整然と説明してあります。

http://d.hatena.ne.jp/yhr/20141017/p1

要はJava 8にバージョンアップした時に余計なパスが加わって、64bitのeclipseが32bitのjavaw.exeを呼んでいるということ。
eclipse.iniに元の64bit版への絶対パスを指定してやればいいんですね。

…なんですが、私の環境を全検索しても64bit版javaw.exeってのが居ません。
今までどうやって起動してたんだい君は?

気持ちが悪かったのですが、64bit版JDKを再インストールしたところ、eclipse.iniの変更なしで起動するようになりました。

なにかやらかしたのかなぁと記憶をたどると、思い当たることが一つ。
なにかする度にJavaのアップデートしろしろ言われるので、年末の休みに入ったあたりでポチってしまったかもしれません。
Javaをバージョンアップした時に、古いものを削除しますか的なことを言われたような気もします。
きっとその時にアンインストールしちゃったんですね、そういうことにしておきましょう。

アップデートのトリガーになったブラウザが32bitアーキテクチャだから、という説明がありましたが、それにしても64bit版OSに32bit版を導入してしまう動きがおかしい気がします。