凪瀬 Blog
Programming SHOT BAR

目次

Blog 利用状況
  • 投稿数 - 246
  • 記事 - 0
  • コメント - 1364
  • トラックバック - 176
ニュース
  • 2008-11-08 わんくま富山勉強会#1 開催。参加者募集中
    2008-08-09 わんくま東京勉強会#23 「C#登場前夜」
    2008-04-01 *で始まるタイトルはエイプリルフールネタです
    2008-01-26 わんくま東京勉強会#16
    ライブプログラミング
    2007-12-08 わんくま名古屋勉強会#1
    「わんくま初めてのJava」
    2007-07-28 開店
広告
  • Java開発者募集中
  • 経歴不問
  • 腕に自信のある方
  • 富山市内
  • (株)凪瀬アーキテクツ
アクセサリ
あわせて読みたい
凪瀬悠輝(なぎせ ゆうき)
  • Java技術者
  • お茶好き。カクテル好き。
  • 所属は(株)凪瀬アーキテクツ
  • Twitter:@nagise

書庫

日記カテゴリ

 

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
コメント
  • # re: 実は単に型推論が欲しいという話
    かつのり
    Posted @ 2008/03/07 16:16
    前半のreturn this;という話は、voidの戻り値の場合は暗黙的に自分自身のインスタンスを返す、的なサポートがあればうれしいなと昔から思っていました。

    Genericsに関してはファイル内でのみ有効なエイリアス構文とかあると、随分楽になりそうですよね。

    import FooList = ArrayList<Map<String, String>>;
    見たいな感じで。
  • # re: 実は単に型推論が欲しいという話
    myugaru
    Posted @ 2008/03/07 18:15
    凪瀬さんの意見に賛成です。
    ってかコード書く上でインターフェース部分のいわゆる宣言ってのは1回しか書かないですよね。プログラム全体から考えたらたった1行の事くらいで冗長とか言ってるのが笑えます。
    むしろ入り口、出口にあたるインターフェースはしっかりとした冗長すぎる肩書きを書いて内部ブラックボックスを説明すべきだと。
    まあ人間でも長い肩書きの名刺使ってるのはそのためなんでしょう?w
  • # re: 実は単に型推論が欲しいという話
    myugaru
    Posted @ 2008/03/07 18:23
    あとこれは凪瀬さんが言及しないと言ってたほうの件。
    addの戻りがコレクションを返すべきってのはすっごくミニマムな要件だと思います。ってか先行きの言語とか世の流れとか見てないと重います。
    確かに今のみた感じだとappendみたいにコレクション本体返せばメソッド連結できるから便利みたいに思うかもしれませんが、これにはいくらでも反論があります。
    1.状態変更するメソッドは状態変更に徹すべき。逆に読み出しアクセッサは状態変更はしない。
     これは結構しっかりしたシステム組んでるところならプログラミング規則にも良くかかれています。
    2.リストなどへの値追加ってのはたいてい値をノード状のクラスなりのラッパーへ包んで取り込むことが結構あります。(Nextとかの次ノードポインタ含めたりね)。なのでたとえばaddの結果でノードを返すみたいな要件だって生まれる可能性はあります。

    (今日は結構ちゃんとした日本語で意見が書けた気がするw)
  • # re: 実は単に型推論が欲しいという話
    凪瀬
    Posted @ 2008/03/07 18:24
    エイリアスかー。あんまり長いと使いたくなる気持ちも分かりますが…。

    > インターフェース部分のいわゆる宣言ってのは1回しか書かないですよね。

    その1回の部分の話じゃなさそうなのですよね。元記事は。
    ローカル変数の場合ぐらいなんですよ。型宣言とnewでのインスタンス生成が並ぶのは。
    だから、冗長というのも不当ではない。ローカル変数を宣言するたびにしているから面倒という思いも募るのがわかる。

    で、自分はもともとローカル変数でもきっちり書けばいいじゃん、って考えだったんですが
    エントリ書いていて、ローカルメソッドだとデメリットが少ないからvarの型推論もあるいはアリなのかな、と思いを改めました。

    ただ、varで育つとインターフェース定義で躓くかもしれないんですけどね。
    そんときはそんときか。
  • # re: 実は単に型推論が欲しいという話
    通りすがり
    Posted @ 2008/03/07 19:34
    > // Piyo extends Hogeとする
    > List<? super Hoge> hogeList = new ArrayList<Piyo>();

    これコンパイル通らないと思うんですけど。
    hogelistは、Hogeとそのスーパークラスを許容するってことだから、そこにHogeのサブクラスであるPiyoを型パラメータに指定するのはNGですよね?Hoge extends Piyoの間違い?
  • # re: 実は単に型推論が欲しいという話
    凪瀬
    Posted @ 2008/03/07 19:49
    extendsの誤りですね。
    ご指摘感謝します。
  • # re: 実は単に型推論が欲しいという話
    melt
    Posted @ 2008/03/07 23:10
    >メソッド引数やreturn値の型、またインスタンスフィールドは厳密に抽象型を用いるべき
    Java の新しいことは全然分からないんですが、これは var 使えないのでは……。
  • # re: 実は単に型推論が欲しいという話
    中博俊
    Posted @ 2008/03/07 23:32
    Javaでのお約束的なインターフェイスの重視と、.NETの軽視(というか視点が違う)についてなぎせさんの意見やいかに。
    私としては無意味な面倒はごめんなので、すべてvar構文で済ませる局面ですね。
  • # re: 実は単に型推論が欲しいという話
    通りすがり
    Posted @ 2008/03/07 23:42
    粘着するようで申し訳ないですが、List<? extends Hoge>としてしまうと、hogeListには要素を追加することができないので、あまり意味のない変数になってしまいますよ。

    メソッドの引数などならともかく、変数宣言時にこの記事のように型パラメータにワイルドカードを使っても、あまり嬉しくないのでは?

    個人的な感覚では、変数の宣言時と値の代入時の型パラメータは一致するケースのほうが多いのではないかと思います。ですから、同じことを重複して書くことを強要される型パラメータの記述は、冗長だなと私は思います。google-collectionsなどのライブラリを使えば解決できることではありますが。
  • # re: 実は単に型推論が欲しいという話
    凪瀬
    Posted @ 2008/03/08 0:02
    本文中で言っていますが、インターフェースはきっちり定義しろ、と。
    んで、ローカル変数定義はvarでもなんでもいいんじゃないの、というのが現在の意見。
    .NETでもインターフェース部分に型推論は使えないでしょう?

    > メソッドの引数などならともかく

    だから、「ところで、そのメリットってレアケースじゃないの?」って段で
    あんまり嬉しくないよねって言っているじゃないですか。
  • # re: 実は単に型推論が欲しいという話
    通りすがり
    Posted @ 2008/03/08 1:51
    私は、変数の型に抽象型を用いるという話(型と実体の表記は冗長か?)と、Genericsの型パラメータの話(ジェネリクスの型パラメータの記述は冗長か?)は、別の問題として捉えていたので、最後の段の話は変数の型についての言及と勝手に解釈していました。どうやら私が誤読していたようですね。

    粘着して申し訳ありませんでした。
  • # re: 実は単に型推論が欲しいという話
    中博俊
    Posted @ 2008/03/08 11:15
    インターフェイスに型推論が使えないのはまぁ特に意味のあることだとは思っていないのですが(動的にすぎるから)位の意味だとは思います。

    >ただし、外部とのインターフェースとなる部分、つまり、メソッド引数やreturn値の型、またインスタンスフィールドは厳密に抽象型を用いるべきです。

    ここがいまいちなぜ抽象型を持ち上げるのかぜひ別エントリで書いてほしいと思います。
    私はあくまで不要だとおもってますから。
    #全然いらないという意味ではなく、必要に応じて程度でいいと思っている程度
  • # 具象型でインターフェースを書いた場合のデメリット
    凪瀬 Blog
    Posted @ 2008/03/09 10:09
    具象型でインターフェースを書いた場合のデメリット
タイトル  
名前  
Url
コメント