DIコンテナ部分の移植
大方完了した。まだすべてのテストが完了した訳では無いが、次のステップに進める段階ということだ。
移植の際に苦労したことをメモしておく。
私はジェネリクスデビューがC#2.0だったので、Javaのジェネリクスには相当苦労している。最も困惑するのはJavaのそれは実行時に非ジェネリックな型から独立していないことだ。また、invariant/covariant/contravariantの考え方※の違いも頭を悩ませる。
- 似て非なるメソッド
きちんと確かめて使わない私が悪いのだが、同じ又は同じ目的のための使うと思われるメソッドの振る舞いが両プラットホームで全く違う場合があり、それが混乱を招く。まあ、これこそがポーティングの罠なのだが。
例えばHoge型が実装しているインタフェースを取得するメソッドは
・C#
Type[] interfaces = typeof(Hoge).GetInterfaces();
・Java
Class[] interfaces = Hoge.class.getInterfaces();
とほとんど同じだが、C#は対象となる型だけではなく、継承ツリーを遡って実装されているインタフェースを掻き集めるのに対して、Javaは対象の型が明示的に実装しているインタフェースしか取得しない。これはどちらの設計が悪い等の問題ではなく(型のコントラクトを定義するのがインタフェースの大きな役割の一つなので、個人的にはC#の方が正しいと感じる)実装の問題だ。むろん、Javaも再帰等で継承ツリーを辿ればC#と同様の結果を取得できる。
- プリミティブに対応した値型
これもC#にあってJavaには無いものだ。C#を使い始めた頃は値型はいらないと思っていたのだが、つかうにつれその有難さと必要性を感じるようになった。Javaに戻るとプリミティブに対応するラッパーがあるだけなのでいろいろと面倒なことが多い。ただ、新たに導入されたJavaのAutoBoxingは以前に書いたエントリの通りで癖はあるがあるとやはり便利だ。
- foreach大好き
最初は単なるシンタクスシュガーだと訝しげだったが、慣れると直感的で書きやすくなるので、Javaでもがんがん使っている。ちなみにJavaの場合はforeachではなく拡張for文での実装になる。
・C#
int fibarray = new int { 0, 1, 2, 3, 5, 8, 13 };
foreach (int i in fibarray)
{
System.Console.WriteLine(i);
}
・Java
int[] fibarray = { 0, 1, 2, 3, 5, 8, 13 }; for (int i : fibarray){ System.out.println(i) }
こういう新構文は、最初は「性能の劣化が不安」とかで反対する人が必ずいるが、経験上、特定の構文の性能の問題は最新の仮想マシンで必ずターゲットになり改善されていくものだし、変に怖がる必要は無いと思う。便利だと感じたらどんどん使うほうが良い。
あと、強く感じたのはMicrosoftの言語系にいつも感じることだが、言語としての綺麗さよりも実用性を優先した機能と割り切りを多く持つということだ。
また、Javaに比べて後発である有利さはあるが、それにしてもコレクション周りやXML周りなどはC#のほうがうんと使いやすいのも感じる。(ジェネリクスと非ジェネリクスのコレクションのネームスペースを分けたことは大胆だが正解だったと思う)
※まだいまひとつ整理がついていないので詳しく書けないのでもどかしい。