凪瀬 Blog
Programming SHOT BAR

目次

Blog 利用状況
  • 投稿数 - 260
  • 記事 - 0
  • コメント - 46866
  • トラックバック - 192
ニュース
広告
  • Java開発者募集中
  • 経歴不問
  • 腕に自信のある方
  • 富山市内
  • (株)凪瀬アーキテクツ
アクセサリ
  • あわせて読みたい
凪瀬悠輝(なぎせ ゆうき)
  • Java技術者
  • お茶好き。カクテル好き。
  • 所属は(株)凪瀬アーキテクツ
  • Twitter:@nagise

書庫

日記カテゴリ

 

@ITの 文字列をequalsで判定する時 で行われた議論に対する私なりの考察の第二回です。

議論のテーマは

  • "hoge".equals(str)の構文的な分析
  • "hoge".equals(str)は読みにくいのか?
  • "hoge".equals(str)は読みやすいのか?
  • NullPointerExceptionへの対応
  • ヒューマンエラーのリスクの推定

です。前回エントリでは構文的な分析について述べました。

"hoge".equals(str)は読みにくいのか?

そもそも「読みやすい/読みにくい」とは何かというのが難しいテーマですね。 その人がどのような知識をもっているか、どのような思考回路をしているのか、 そういったもので変わってきますから、「主観」の一言で済まされてしまいそうな話題です。

通常、Javaの言語を学ぶ過程で、変数というものは早期に教えられます。 そして、メソッドの呼び出しについても早期に教えられることでしょう。

プリミティブ型とオブジェクト型の違いは初期に教わると思いますが、 それがしっくりくるまでには時間を要します。 型とインスタンスの概念について、自信を持って回答できるまでには相応の訓練ないし経験が必要です。

定数に対するメソッド呼出しが不可能と思いこむ理由

さて、「オブジェクト型 - プリミティブ型」というパラダイムと、 「変数 - 定数」というパラダイムを合わせることで4つの事例が導出できます。

// オブジェクト型 - 変数
String str = "hoge";
str.toString();

// オブジェクト型 - 定数
"hoge".toString();

// プリミティブ型 - 変数
int i = 0;
i.toString();

// プリミティブ型 - 定数
0.toString();

このうち、プリミティブ型の2つのコード例はコンパイルエラーとなります。

注目したいのは、オブジェクト型とプリミティブ型の「変数」の場合のメソッド呼出しは いずれもコード的には同一の外見をしている点です。変数名が同じであれば

String hoge = "hoge";
hoge.toString();

int hoge = 0;
hoge.toString();

というように、まったく同じ外見をします。そして、一方はコンパイル可能で、一方はコンパイルエラーとなります。 対してプリミティブ型の定数に対してのメソッド呼出しというのは外見がそもそも違うので容易に識別できます。

256.toString();
0xCAFEBABE.toString();

ですから、「定数に対してメソッド呼出しをすることはできない」と理解してしまうことは自然でしょう。 文字列定数という「定数だがオブジェクトである」という例外を知るまでは。

文字列定数のメソッド呼出を知らずともあまり困らない

オブジェクトに対してメソッド呼出しを行うことは、初期に理解していることと思います。 そうなると、「文字列定数のメソッド呼出し」を理解するために必要なのは、 文字列定数が「特殊な何か」という理解から、「オブジェクトの一種」であるという汎用的でより抽象的な 概念での理解ではないかと思われます。

といっても、そんなに難しい話ではなくて、「"hoge"はStringの変数に代入できることからも 分かるようにオブジェクトであるわけだから、"hoge".toString()といったメソッド呼出しが可能だ」という程度の 説明で「そうなんだ」と理解できる程度には簡単な話題です。

ただ、理解がたやすいことと、広く知られているかは別問題。 触れる機会がなければそのような概念を理解する必要はなく、 「定数からメソッド呼出しはできない」という誤った理解でも問題がない。 自分の知りうる世界ではその理解でもなんらの矛盾を起こさないのですから。

オブジェクト型のいろいろ

触れる機会がないなら、誤解していようが矛盾に遭遇しないので対して問題が発生しないことは述べました。

オブジェクトであればメソッド呼出しができるわけですが、誤って理解していても困らない例をもう少し挙げてみましょう。

int[] array = new int[]{1, 2, 3};
array.toString();

これは、配列もまたオブジェクトである例です。変数arrayはプリミティブ型であるintの配列ですが、 toString()といったjava.lang.Objectのメソッドを呼び出すことができます。

「配列もまたオブジェクトである」という正しい知識を持たず、「配列は配列」と理解していてもあまり困ることはありません。 配列をjava.lang.Objectとして扱わなければならない状況というものがそもそもレアですから、 誤った理解をしていたとしても、そこから生まれ来る矛盾に遭遇することはなかなかありません。

また、JavaSE 5.0からは列挙型というものが用意されました。

public enum Hoge {
  ENUM_1,
  ENUM_2,
  ENUM_3
}

この列挙型もまたオブジェクトですから、

Hoge.ENUM_1.toString();

というように記述可能です。が、列挙型の場合は見た目が定数っぽく見えないですから、 「定数はメソッド呼出しができない」という誤った理解でも違和感を感じないかもしれません。

読みにくさの理由

以上のことをまとめてみましょう。

(1)学習の過程の関連からstr.toString()が理解できるが、"hoge".toString()が理解できない理解状況存在する。 これは"hoge".toString()は理解できるがstr.toString()が理解できないという理解状況にある人というものが 存在しないことから納得できる事項であるでしょう。

(2)上記「定数はメソッド呼出しができない」の誤解をしている可能性。 プリミティブ型のリテラルからのメソッド呼出しはできませんが、仮にそのような表記をすれば 外観からして違うものとなることは述べたとおりです。 また、このような誤解をしていたとしても、困らないことが多いと述べました。つまり、誤解が訂正されない可能性があります。 この誤解が先入観として沁み渡っているとすれば、文字列リテラルに対してのメソッド呼出しは異質に思えるのではないでしょうか。

この(1)および(2)から「読みにくい」という感情をある程度は説明できると思われます。 またこの他に、今までの経験から違和感を覚える可能性が考えられます。

(3)「定数からメソッド呼出しをすることに触れていない」可能性。 この場合、なじみのない記法ということになりますから、「違和感」を感じると思われます。 そのようなソースにたくさん触れるとなじむ可能性があります。

(4)「若者の言葉の乱れ」の話題ように、自分の習慣にない記法に対して嫌悪感を持つ可能性。 私も「重複」を「じゅうふく」と読むのだけはどうにも許せないのですが、 このように自分のやらないことに対して嫌悪感を持つのは人間心理としては自然なことです。

これらの事象から「読みにくい」「違和感を覚える」「気持ち悪い」という感想が出るのではないでしょうか。 これらいずれにも当てはまらないが、「違和感を覚える」人もいるかもしれません。 ここで挙げたものは、「違和感を覚える原因として考えられること」ですが、 当然、そのすべてを列挙しているわけではありません。

しかし、こういった原因によって感情が作られると分析することで、 「そういった感情を覚える自分」といったものに対しても向かいやすくなります。

このエントリでとりあげられなかったこと

このエントリでは文字列リテラルのメソッド呼出しという観点で分析をしました。 そのため、メソッドの例としてはtoString()を用いています。 @ITの方では意味による「読みにくさ」が指摘されています。

つまり、"hoge".equals(str)というのは「"hoge"がstrと等しい場合」という意味になりますね。 日常の思考では「strが"hoge"と等しい」という考え方をするため、捉えにくいという意見です。

この点の考察はまた別途行いたいと思います。

投稿日時 : 2008年2月16日 22:00
コメント
  • # re: 文字列リテラルのメソッド呼出し その2
    myugaru
    Posted @ 2008/02/16 22:33
    こんばんわ。私自身の今回のどっちが良いかという話の意見はとりあえず読みやすいほうということでstr.equals("hoge");に一票入れたいと思っています。
    で、ついでというか、この話を見て2つ思い出した事がありますので書きます。
    1つ目。
    C言語でif文中で
    if(2==x){}
    みたいに定数を左に書くのが常になっていたのに初めて遭遇した日のこと。これの理由はif文中の=が一つ減って代入文にうっかりなってしまわないためだと先輩に聞いた。
    2つ目。
    buff[4]
    という記述を
    4[buff]
    でも同じだ、という乱暴な話。これはコンパイラがどちらもbuff+4のアドレスの中身を参照するからだという話。
    私はこれらの話を含めてずっと思ったのは、人間というのはミスをしやすい。だけどそれを回避するために思考の妨げるようなコードをわざわざ書くのはどうだろう?という意見でした。しかしC言語ではそれらは言語上そうやって回避するしかありませんでした。それがたとえばC#になるとif文中には代入文はかけなくなっています。当然buff[4]は4[buff]とも書けません。言語自体がそうやって色々とある意味「まずい」回避策を「しなくてよい」ように進化していくべきだと思っています。
    そういうことも踏まえて今回の話を見てみるとそれぞれの書き方の意見があるようですが、それらを言語自体が何らかの(私にはそれがどういう方向かはわからないですが)いずれは何かの形で進化してやらなくてもいい気を回さなくて済むような方向に向かうのではないかって思ったりしています。
  • # re: 文字列リテラルのメソッド呼出し その2
    myugaru
    Posted @ 2008/02/16 22:37
    すみません。buff[4]の話はbuff[4]より4[buff]のほうがあるコンパイラは実行速度の速いアセンブルコードを吐くんだという乱暴な話だ、という部分が抜けてました。
  • # re: 文字列リテラルのメソッド呼出し その2
    かつのり
    Posted @ 2008/02/17 0:05
    バイトコードを比べましたけど、
    IFNULLというインストラクションが実行される差しかないですね。
    本当に微々たる差しかないので、
    コードの読みやすさで判断するのはありなんですね。

    つーか、EclipseのASMプラグインを使っていますが、
    バイトコード見るには最高ですよ。
  • # re: 文字列リテラルのメソッド呼出し その2
    凪瀬
    Posted @ 2008/02/17 10:51
    単に「str.equals("hoge")が読みやすい、1票」という捉え方ではいけないということについても書いたつもりだったのですが、なかなか伝わらないものですね。
    理由なしでの意見は「そう言った人がいた」以上の資料とはならない点を理解していただきたい。
    大事なのは「なぜ?」という部分ですね。

    if (2==x)のような記述など、記法による特徴やその評価については後のエントリで行うので、
    その際にまたご意見いただけると嬉しいですね。

    > IFNULLというインストラクションが実行される差しかないですね

    まさに挙動の違いそのものの部分ぐらいしか違わないんですねぇ。
  • # re: 文字列リテラルのメソッド呼出し その2
    myugaru
    Posted @ 2008/02/17 14:37
    ああああ!読みやすいかどうかの主観についての話題ですね。「そこが問題だろ!>自分」私としたことが突っ込みどころ満載です・・・すみません浅はかでした。でも懲りずにまた寄らせていただきます><;
  • # 文字列リテラルのメソッド呼出し その3
    凪瀬 Blog
    Posted @ 2008/02/19 1:05
    文字列リテラルのメソッド呼出し その3
タイトル
名前
Url
コメント