補足(いいわけ?)000


グローバル変数(2002/1/14)

例によって... と言うとアレですが、あの巨大匿名掲示板 2ちゃんねるに、 Java謎+落とし穴徹底解明のスレッドが立っていますね。

「Javaの謎+落とし穴徹底解明」を読まない奴は厨房:
http://pc.2ch.net/test/read.cgi/tech/1010575251

細かい突っ込みですが、スレのタイトル違ってます。 正確な書名は「Java 謎+落とし穴徹底解明」であって、「の」は入りません。

で、このスレの1さんの以下の意見について、ちょっと補足しておきます。

内容的には結構異論が出る部分も多いが・・・
「Javaにはグローバル変数がないなんて大嘘」という記述はちょっとおかしい
グローバルとパブリックは全く違うだろ。
どこからでも見れるからといって「グローバル変数」とは呼ばないはず。

該当する記述は、p.14の以下の記述だと思われます。

「Javaにはグローバル変数がない」(これもウソですね)だの、 Cのような従来型の言語を知っている人からすれば...

理由の説明がないので1さんの真意はよくわかりませんが、 1さんにとってのグローバル変数の認識が 「識別子がトップレベルのスコープにある変数のこと」であるのだとすれば、 確かにJavaにはグローバル変数はないということになるのかもしれません。

p.66にあるように、

  class Global {
      public static int variable;
  }

と宣言した時、variableは名前空間上トップレベルにはありません。

しかし、「Global.variable」と書けば、 実質グローバル変数と同じように使えてしまいます。 なのに、「グローバル変数がない」はウソだろう、 と私は主張しているわけです。

つまり、「グローバル変数がある」と言いたいわけではなく、 「グローバル変数がない」はまずいだろう、ということです。

# これはポインタの話でも同じで、「ポインタがある」というのは主張の
# 本題ではなくて、「ポインタがない」はまずいだろう、と。
# これについてはまた今度書きます。

Java好きな人が「Javaにはグローバル変数がない」と主張する時、 たいていは以下の論法がセットになっているものです。

  グローバル変数は邪悪である。
       ↓
  Javaにはグローバル変数がない。
       ↓
  だから、Javaは優れた言語だ。

しかし、実際には、public staticなフィールドは、 グローバル変数と同じように使えてしまいます。 というわけで、以下の記述につながるわけです。

p.66 末尾から引用(強調は本の通り):

publicでstaticなフィールドは、プログラム中のどこからでも参照できますから、 結局グローバル変数とまったく同じ便利さと まったく同じ邪悪さを併せ持ちます。

p.67のPOINT

staticでpublicなフィールドは、結局グローバル変数と同じ。

ところで、この点について、私は掲示板の方で

publicであるだけでなく、staticであることも入れて欲しい...

と書きました。

1さんは、私が実質グローバル変数と同じだと主張しているものが 「publicでstaticなフィールド」 であることを理解した上で、名前空間の点から反論されているのかもしれません。

しかし、2ちゃんで反応する人が本を読んでいるとは限らないわけでして...

58 :デフォルトの名無しさん :02/01/11 22:22
  >どこからでも見れるからといって「グローバル変数」とは呼ばないはず。
  staticを使えば、グローバル変数ができるよ
  って、これは既出?

既出っていうか... 本の方にはそう書いてあるわけです。

59 :デフォルトの名無しさん :02/01/11 22:27
  >>58
  アクセス制限をつけられるグローバル変数なんてグローバル変数
  じゃないやい!

staticフィールドはprivateにもできるよ、ってことなんでしょうけど、 1さんは最初から「パブリック」と書いているわけで、 58さんもそれを受けての発言なんじゃないかと思うんですが(知りませんが)。

その後には、本どころかそこまでのスレの流れも完璧に無視した、 まぬけな反応も出てきてますし。

61 :Kusakabe Youichi :02/01/12 01:32
  >staticを使えば、グローバル変数ができるよ

  はつみみです。

  public class foo {
    private static int hoge;
    ............
  }

  このグローバル変数hogeには他のクラスからどうやってアクセスするのでしょうか?

# 2002/1/15 04:22 訂正情報
# 「2ちゃんねる」の方で指摘がありまして、元の記述は58さんに失礼にあたる
# 部分がありましたので、訂正しました。
# 私の意図は、「1さんは本を読んでいて、58さんの指摘は本の方には書いてある」
# ということであって、58さんが本を読まずにレスしたことをとやかく言いたかった
# わけではありません。誤解を招く表現をしていまい、すみませんでした。

それにしても、 ポインタ完全制覇のスレが立ったときもそうだったんですけど、 なんでこう、すぐに「自作自演」と決めつけられるんですかねえ (^^;

私は、自分の本に関して、「2ちゃんねる」 に書き込んだことは過去一度もありません。 もちろん、上記のスレッドを立てたのも私ではないですし、 このスレに書き込んだこともありません。 マ板の「 推薦図書/必読書のためのスレッド7」の155も私ではありません。 ...ええと、 ここに『私は生まれてこの方「2ちゃんねる」 に書き込みをしたことはただの一度もありません』 と書いたときとか、 ここに 『私は最近は結構2ちゃんを見てたりする(^^; 書いてはいませんが』 と書いたときと表現が若干違うような気もしますが気にしないでください(とかいう)。

まあ、疑われるところまではしょうがないというか勝手にしてくれなんですが、 「推測」が、 一瞬にして「事実」にすりかわっている人がちょくちょくいるように見えるのは...

いわゆる「トンデモ本」にありがちな「陰謀論」をホーフツとさせますな。


ポインタ(2002/1/26)

「グローバル変数」の項で、 『「ポインタがある」というのは主張の本題ではなくて、 「ポインタがない」はまずいだろう、と。これについてはまた今度書きます。』 なんてことを書きました。 てなわけで、ちょっと遅くなってしまいましたがここで書いてみます。

---って、一番言いたかったことは既に書いてしまっているわけで、 要するに『「ポインタがある」というのは主張の本題ではなくて、 「ポインタがない」はまずいだろう』に尽きるんですが。

本の方でも説明していますが、C/C++では、 構造体やクラス型の変数を普通に宣言(定義)するだけで、その型のインスタンスを 格納できる「箱」が用意されます。それに対し、Javaでは、

  Hoge hoge;

と書いても、確保されるのは「Hogeを指し示す値」を格納するための「箱」だけです。 これは、C/C++で書けば、

  Hoge *hoge;

と書くことに相当し、ゆえにhogeは、 C/C++用語で表現するなら「ポインタ」である、と言えます。

よって、少なくともC/C++あがりのプログラマに説明するには、 「クラスと配列に関する限り、Javaにはポインタしかない」と言うのが 最も理解が早いと思います。

もちろん、C/C++のポインタと、Javaのポインタとは違うところもあります。 「Javaにはポインタがある」と書くことで、 「JavaにはC/C++と同じポインタがある」 と誤解する人が出てこないように、2章のごく初めの方に 「2.1.2 C/C++のポインタとJavaのポインタとの違い」という項を入れました。 そんな誤解をする人が本当にいるのかどうかは知りませんが。

もっとも、最近はC/C++からJavaに移行する人は減りつつあるようです。 いきなりJavaから始めるプログラマもたくさんいるでしょう。 そういう人にとっては、私がいくら「ポインタ、ポインタ」と連呼したところで 「ハア?」でしかないことでしょう。 それは私も認識していますが、 そういう人には「ポインタ」だろうが「参照」だろうが 「どっちでもいい」のではないかと思います。

ということなら、2章まで限定でJavaの「参照」を「ポインタ」と呼ぶことは、 益こそあれ、さしたる害はないはずです。 いずれにしても「何かを指し示すモノ」という概念は、 Javaをやる上では絶対に避けられないのですし。

まあ、害と言えば、あんまり「ポインタ、ポインタ」と連呼すると、 2ちゃんねる 「Javaの謎+落とし穴徹底解明」を読まない奴は厨房スレの >>128さんに言われているように、 「何をそんなに声高に叫んでいるんだ?」という「印象」を読者に与える、 という危険は確かにあるわけですけどね(^^;

実際、私も「我ながら子どもじみてるなあ」と思ってはいて (2ch用語で言えば「厨房」くさいってとこでしょうか)、 それは本の方のp.145に書いています。

Javaでは、ポインタ相当品のことを「参照」と呼びます。 それはそれでよいでしょう。 私だって、「Javaにはポインタがない」なんて言説さえ存在しなければ、 「Javaにはポインタがある」なんて子供じみた 主張をしなくて済むんですけどねえ。

...いや、つくづく思うんですが、「Javaにはポインタがない」 という主張をする人の意図っていったい何なんでしょう?

C/C++プログラマなら、「ポインタがない」なんて言われたら混乱するばかりだし、 そうでない人には「どっちでもいい」話ですし。

C/C++だと、ポインタの扱いをしくじって 領域破壊などの見付けにくいバグを生むことがありますが、 Javaはそういうことがない安全な言語です。 それは事実ですが、そういうことを主張したいのなら、 「JavaはC/C++と違って実行時チェックをちゃんとやる言語だ」 と言えばよいはずです。「ポインタ」があろうがなかろうが、 たとえば配列の範囲チェックをしなければ、なんぼでも領域破壊を起こします。

C/C++と違って、メモリを直接触ることができない高級な言語だ、 ということを主張したいのかもしれませんが、C/C++においても、 「ポインタ」を「メモリアドレス」だとして認識しなければならないことなど ほとんどないはずです(デバッグ時を除く)。 というか、そんなことを認識しなければならなくなった時点で、 おそらくそのプログラムは移植性が失われていることでしょう。 組み込み系やOSなど、「メモリアドレス」を強く意識しなければならない プログラムならいざ知らず、アプリケーションプログラマなのに Cの「ポインタ」がどうしても「メモリアドレス」にしか見えない人ってのは、 今までそうやって移植性の低いプログラムばっかり書いてきたんでしょうか。 「抽象的なレベルでの思考ができない」というのは、 プログラマとしては致命的だと思うんですが。

本の中であれだけ「ポインタ、ポインタ」と連呼しておいてなんですが、 私は別に「ポインタ」という言葉にこだわりがあるわけではないのです。

「Javaにはポインタがない」 という言説による意味的な誤解を問題視しているだけです (意味的な誤解の実例は本の方で挙げました)。

p.148(2章のラスト)より引用:

---本書をここまで読んだ方なら、Javaにおけるクラス型や配列型の変数が、 「何かを指し示すモノ」であることはすでに理解されていることでしょう。
それさえ理解してもらえれば、私としては、別に「Javaにはポインタがある」 と思っていただかなくてもかまいません。 C/C++のポインタとJavaのそれとの違いを指して、 「Javaにはポインタがない」と主張するのもよいでしょう。 意味的な誤解さえ招かないようにしていれば。
本書でも、この先は「ポインタ、ポインタ」と連呼したりはせず、 オトナの態度でもって「参照」と呼んでいきたいと思います。

まあ、Javaの参照は C/C++のポインタに相当するということを説明するだけならともかく、 (2章までの)地の文の中でまで、 いちいちJavaの参照を「ポインタ」と呼ぶ必要はなかったかもしれません。 実はそれは2章を書き始める前に少なからず迷ったのですが、 「3章からは参照」というネタを使いたかった--- ってのはもちろんウソで(いや本当に)、 「POINT」の欄などで、「Javaでは、オブジェクトの配列は、 オブジェクトへのポインタの配列になる。」(p.111)と書くような場合、 「ポインタ」と書いた方が意味がはっきりすると考え、 2章まではあえて「ポインタ」と書くことにしました。

また、「Javaにはポインタがない」なんてウソだ、ということは、 最近ではかなりあちこちで言われていることですので、 >>28さんに言われているように、 「何を今更」という気はしないでもありません。 しかし、逆に言えば、このスレッドがこんなに伸びたということは、 このネタもまだ有効だったか、とも思います。

ところで、件のスレッドを見ていて思ったんですが、 Javaの参照とC++の参照を混同している人って、実は結構多いんでしょうか。

それについては、本の中ではp.76で

ちなみにC++の「参照」は、Javaの「参照」とは別物であり、 Javaの参照はあくまでC++の「ポインタ」に相当します。

と言及していますが、 もっとあちこちに、執拗に、何度も何度も書いておくべきだったかも。

C++で「参照」と呼ばれている機能は、むしろ「別名」と呼ぶべきものでしょう。 Javaなどの参照とはまったく別物です。 ---ということは、実は「ポインタ完全制覇」には書いたのですが(p.248)、 どちらかといえば「Java謎+落とし穴徹底解明」 の方に書くべきことだったかもしれません。反省しています。

それから、2chの方ではcall by referenceの話も出てきていましたが、 C++はともかくとして、Pascalの仕様書では 「呼び出された側で値を変更すると呼び出し側に影響を与えるような引数」 のことを「参照渡し」とは書いておらず、あくまで「変数パラメタ」と 呼んでいるようです(ISO 7185/JIS X3008 6.6.3.3)。 もっとも、Pascalの仕様書はそれほど丁寧に読んだわけではないので どこかに「参照渡し」という記述があるかもしれませんが。

もちろん、Pascalの「変数パラメタ」は、ほぼ全ての処理系において その変数のアドレスを渡す形(すなわちcall by reference)で実装されていると 思うのですが、単に「変数パラメタ」を実現したいだけであれば、 必ずしもcall by referenceにする必要はありません。たとえば、 Cの素朴な関数呼び出しの実装を考えたとき、 スタックから引数の分の領域を除去する前に、 呼び出し側の変数に引数の値を代入するようなコードをコンパイラが生成すれば、 変数パラメタに相当する機能は実現できます。

Adaだと、各引数にin, in out, outという指定ができますが、 その渡し方も規格である程度規定されているようで、 Ada83の規格(ISO8652/JIS X3009)の6.2には以下の記述があります。

スカラ型のパラメタの場合、これらのパラメタの受け渡しの効果は、 複写によって達成する。各呼出しの最初に、 inモード又はin outモードの実パラメタの値は、結合した仮パラメタに順複写する。 in outモード及びoutモードの仮パラメタの値は、 副プログラム本体の正常な完了後、結合した実パラメタに逆複写する。 アクセス型のパラメタの場合、三つのモードすべてについて順複写を適用し、 in outモード及びoutモードについて逆複写を適用する。

配列型、レコード型又はタスク型のパラメタの場合、処理系は、 スカラ型の場合と同じく、 複写によってこれらのパラメタの受渡しの効果を達成してもよい。 (中略) 他の方法として、処理系は、 これらの効果をアドレス渡しによって達成してもよい。 (中略) プログラムの実行の効果が、処理系がどの機構を選ぶかに依存する場合には、 そのプログラムの実行は潜在的誤りとする。

スカラは小さいから複写で渡すが、 配列やレコード(Cで言う構造体)のような大きなものはアドレス渡しでもよい、 ということのようです(最後の一文が気になるなあ...)。

だから何だと言われそうですが(^^; Pascalで変数パラメタと呼ばれているような機能は、 ポインタやら参照やらの話とは分けて考えるべきなんじゃないかと思います。


海外在住の方より(2002/3/17)

少し前のことですが、ニューヨーク在住の(日本人の)読者の方より メイルをいただきました。

その方のメイルの要旨を勝手に要約させていただきますが、

  1. p.201にある、
    「Polyline is a Shape」というのはともかく、
    「Subclass is a Superclass 」というのは変ではないか。
    SubclassはSuperclassではない。 言おうとしていることはわかるけど、最初見たとき変だと思った。
  2. 「自動車 has a エンジン」とあるけど、 「Car has an エンジン」ですね。
    # 「Sorry pointing small miss.」という注がついてました。

まずひとつめの件についてですが、SubclassはもちろんSuperclassではありません。 ですから、「Subclass is a Superclass」というフレーズを見れば、 確かに変に感じるのかもしれません。

一応補足しますが、本にある「サブクラス is a スーパークラス」 という文言において、この「サブクラス」「スーパークラス」は、 あくまで「サブクラスが意味するもの」 あるいは「スーパークラスが意味するもの」という意味です。

日本人の目からすれば、「サブクラス is a スーパークラス」なんて 英語とカタカナちゃんぽんの文章においては、 「is a」自体がほとんど記号的な意味しか持たず(もちろん中学校で習ってますから 意味は知っていますが...)、違和感なく読めるんじゃないかなあ、 と思うんですが...

とはいえ、「『ポリライン』is a 『スーパークラス』」のように 括弧付きにするなどすれば、より誤解を防ぐことができたかもしれません。 この点に付いては反省しています。

ふたつめの問題については、英語で「Car has a Engine」と書いているならともかく、 カタカナで「自動車 has a エンジン」なら問題ないんじゃないかと思うんですが、 どうでしょうか。


「補足(いいわけ?)」の目次に戻る | ひとつ後のページ | 「Java謎+落とし穴徹底解明」のページに戻る | トップページに戻る