JavaScriptのループ その2

前回はJavaScriptで配列のループをする場合はCスタイルのループを使うようにした。
ECMAScript5thをサポートしている最近のブラウザでは配列のメソッドとしてforEachが使える。
こんな感じ
[ 31, 45, 52 ].forEach(function(e) { print(e); });
出力:
31
45
52

forEachのいいところはその1であったfor inのように配列の要素とは関係のないプロパティを列挙しないところ。

JavaScriptのループ その1

JavaScriptで配列をループするときには for in ループは使えない。
例えば、以下のような場合は要素ではなく、インデックスがとり出されてしまう。
# print関数が引数を文字列で出力するとして。
var a = [ 1, 2, 3, 4, 5 ];

for (var e in a) {
  print(e);
}
出力:
0
1
2
3
4

これはJavaScriptの for in ループは配列の要素ではなく、オブジェクトのプロパティを列挙するためのものだからだ。
だから、以下のようにすれば、配列の要素以外のものもとり出されてしまう。
var a = [ 1, 2, 3, 4, 5 ];

a.foo = 'bar';  // どこかでプロパティを設定

for (var e in a) {
  print(e);
}
出力:
0
1
2
3
4
foo

だから、JavaScriptで配列をループしたい場合は素直にこう書く。
var a = [ 1, 2, 3, 4, 5 ];

for (var i = 0, l = a.length; i < l; i++) {
  print(a[i]);
}

でも、インデックスとして使っている変数iのスコープはこのループが存在する関数全体になってしまうという問題が残る。

null許容コレクションと不許容コレクション

Javaのコレクション型の中には要素(Mapの場合はキーも)としてnullを許容するものと許容しないものがある。例えばHashMapはキーと値にnullを許容するけど、TreeMapはキーとしてnullを許容しない。

なんでこんなことになっているかというとTreeMapは要素をソートするためにキーの値を見る必要があるからだろう。普通はソートするためにnullと比較することは出来ないからだ。
逆に言えば、TreeMapはコンストラクタにnull許容なComparatorを渡せば、キーとしてnullが扱えるようになる。

つまり一般的には要素の属性を利用するものはnull不許容で、要素の属性を利用しないものはnull許容と考えればいい。

Javaで空のリストを返す

メソッドからコレクションを返す場合、無効な状態を表すのにnullを返すのではなく、要素数0のコレクションを返すのが良い作法とされている。
でも、その度に新しいインスタンスを作成するのは無駄なようにも思える。
そういう場合はjava.util.CollectionsクラスのemptyListメソッドで空のListインスタンスを取得する方法がある。

if (apple.equals(orange)) {
    return Collections.emptyList();
}

emptyList以外にもemptyMap等あるので、使いたいコレクション型に合わせて、メソッドを呼べばいい。

気をつけなくちゃいけないのはemptyListで返されるListは変更不可能ということだ。
あくまで読み取り用のコレクションを返す場合に使える方法だ。
以下のようにemptyListで取得したListに要素を追加しようとすると例外が発生する。

List<String> strs = Collections.emptyList();
strs.add("orange");
Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.TreeMap.compare(TreeMap.java:1290)
        at java.util.TreeMap.put(TreeMap.java:538)
...

Javaでのraw型の警告対処

Javaで古いソースコードの改修をするときにジェネリクスを使っていないraw型のコレクションを取り扱わなければいけない時がある。
まるごとリファクタリングできればいいんだけど、外部ライブラリになっていていじれない場合は警告を抑制する必要がある。

例えば、以下のようにraw型のListを返してくるメソッドがあったとしよう。
List getList()

これを使って以下のようなコードを書くと
List<String> ls = getList();
このメソッドを使おうとするとコンパイラによって警告が出力される。

警告: [unchecked] 無検査変換

これを防ぐためにはSuppressWarningsアノテーションをつける。
今回の場合は無検査変換なので"uncheked"。

@SuppressWarnings("unchecked")
List<String> s = getList();
アノテーションは変数宣言やメソッドなどにつけることができるけど、getListメソッドを呼ぶ度につけるのはめんどうだし、見た目も良くない。

そこで以下のようなラッパーメソッドを用意する。

@SuppressWarnings("unchecked")
public <T> List<T> getTypedList() {
    return legacyObj.getList();
}
これでSuppressWarningsを書くのは1箇所で済む。