Java が使いにくいのは静的だからではない
という記事を見かけたので思うところを書いておきます
該当記事での論点
論点は大きく二つ。前半の主張の
public Map<String, List<String>> example() {
List<String> list = new ArrayList<String>();
list.add("foo");
list.add("bar");
list.add("baz");
Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("names", list);
return map;
}
まあなんといいますか。Map や List を表すリテラルがないせいで、記述が冗長になってしかたない。どうせ List と Map の 99 パーセントは ArrayList と HashMap なんだから、もうそろそろリテラルを用意してもいいと思う。型指定のせいでリテラルが難しいのであれば、「new ArrayList {"foo", "bar", "baz"};」のような記述を用意するだけでもずいぶん違う。
また や > が 1 行に 2 回出てくるのがうっとうしい。List#add() や List#put() は戻り値が void だが、これは return this とするべき (StringBuffer#append() はそうなっているのにね)。せめてこう書けるようにしてほしかった。
public Map<String, List<String>> example() {
var list = new ArrayList<String>();
list.add("foo").add("bar").add("baz");
var map = new HashMap<String, List<String>>();
map.put("names", list);
return map;
}
と、後半の主張のgetter/setterに関するもの。
この点については言語の問題という認識はJava側にもあって
Java7ではプロパティとして対応される見込み。
なので、ここは取り上げません。
前半の主張は
- Listなどと抽象型とArrayListといった実装型の表記は冗長
- ジェネリクスの型パラメータの表記が冗長
- List#add()などが連鎖呼び出しできないのが面倒
の3本立て。うち、連鎖呼び出しについてもこの稿では取り上げません。
型と実体の表記は冗長か?
「どうせ List と Map の 99 パーセントは ArrayList と HashMap なんだから」というのは
あまりにデータ構造に無頓着すぎると思うわけですが、そういう人の場合、
Listなどの抽象型で宣言するメリットは確かに見えてこないと思います。
List<String> list = new ArrayList<String>();
の場合、変数はList型として定義されているので、具象型のArrayListにだけ宣言されている
メソッドが呼び出されることを防ぐことができます。(*1)
継承階層を用いて型の差し替えを可能としているわけですね。
var list = new ArrayList<String>();
の場合、listは型推論でArrayList型になるから、抽象型の範囲で変数を使うような縛りが効かない。
そのため、後になってArrayListを他のListの実装に切り替えた際にコンパイルエラーが生じる可能性がある。
ジェネリクスの型パラメータの記述は冗長か?
ジェネリクスの型パラメータも似たような話で、<String>といったfinalな型をパラメータに
とる場合は単に冗長にしか思えないかもしれませんが、
継承階層があるオブジェクトをパラメータに取る際は
// Piyo extends Hogeとする
List<? extends Hoge> hogeList = new ArrayList<Piyo>();
といったように型パラメータが異なる場合があります。
そして、具象型でジェネリクスの型パラメータを取ると具象型独自のメソッド呼び出しができてしまったり、
という問題が起こることは型のときの話に同じ。
ところで、そのメリットってレアケースじゃないの?
実は、ここまで引っ張っておいてなんですが、ローカル変数に限って言えばメリットは小さい
のですよね。
つまり、ローカル変数のListをList型ではなくArrayList型の変数として宣言しようが、
メリットが生まれるケースはほとんどない。デメリットになるケースもほとんどない。
ただし、外部とのインターフェースとなる部分、つまり、メソッド引数やreturn値の型、
またインスタンスフィールドは厳密に抽象型を用いるべきです。
ローカル変数内での宣言は習慣付けのためにやっていると言っても過言ではないかもしれない。
だから、いざ本番となったときにちゃんと型が表記できるなら
varの型推論でもなんでも使っていいんじゃないでしょうか。
(*1) 実は
ensureCapacityぐらいしか具象型であるArrayList独自のメソッドってないから、
99% ArrayListしか使わない人にはvarでもあまり困ることはないと思う。
投稿日時 : 2008年3月7日 15:53