はじめに
Java Bronze SEを受験しまして、一応合格しました。ただ得点率が100%ではなかったので、ちょっと悔しいです。
そこでこの記事では、Java SE Bronzeの試験範囲内で、私がよく理解できなかった点を整理します。また、(勉強に行き詰っている誰かのために)一応の解説もつけておきます。(でも、間違っているかもしれません。一応、参考文献は明記します)
1. 用語の定義の混乱
Java SE Bronzeという試験では、「Javaに関する用語の意味」が問われることがあるそうです。いわゆる専門用語というやつですね。しかし現状、この専門用語の定義は、あまり統一されていません。
1-1. カプセル化
例えば、「カプセル化」という言葉の定義を見てみましょう。さて、「カプセル化」とはなんでしょうか。「カプセル化」という言葉はどのように定義されているのでしょう。3つの書籍を比較してみます。
1-1-1.『徹底攻略 Java SE Bronze問題集[1Z0-818]対応』
まずは、いわゆる黒本と呼ばれている、志賀澄人, 山岡敏夫『徹底攻略 Java SE Bronze問題集[1Z0-818]対応』の解説を見てみます。
カプセル化は、関係するデータをまとめ、さらにそのデータを使う処理をまとめて、一つのモジュールとして定義することです。
これによれば「カプセル化」というのは、関係するものをセットにして、関係しないフィールドやメソッドを他のところに移すことだと理解できます。簡単に例えると、「お掃除係のお仕事のマニュアルに、生き物係の作業工程を書かない」みたいなことだと思います。(逆もしかり)
1-1-2.『スッキリわかるJava入門 第3版』
一方で、これとは全く異なる「カプセル化」の説明もあります。中山清喬, 国本大吾『スッキリわかるJava入門 第3版』によれば、「カプセル化」は次のように説明されています。
Javaにおけるカプセル化とは、大切な情報(フィールド)や操作(メソッド)にアクセス制御をかけることにより、悪意や間違いによるメンバの利用を防止し、想定しない利用が発生したならば、その原因箇所を特定しやすくするためのしくみなのです。
これによると「カプセル化」というのは、関係しないクラスから、フィールドやメソッドを利用させないようにすることだと理解できます。簡単に例えると、「お掃除係のお仕事のマニュアルに、生き物係が落書きしないようにする」みたいなことだと思います。
1-1-3.『Java入門編第3版 ゼロからはじめるプログラミング』
さらに、三谷純『Java入門編第3版 ゼロからはじめるプログラミング』を参照しましょう。ここでの「カプセル化」の説明は以下の通りです。
フィールドやメソッドを適切に隠蔽することにより、クラス内部でどのような処理が行われているかを、クラスの利用者が意識する必要がなくなります。クラスの中だけで使用するメソッドにも、外部から呼び出せないようにprivate修飾子をつけておくことが大切です。このようにすることをカプセル化といいます。公開されているメソッドの呼び出し方と戻り値がわかれば、内部を知らなくてもクラスが持っている機能を簡単に利用できる便利さは、プログラミングをする上でたいへん大きなメリットです。
こちらによると「カプセル化」というのは、クラス内部の処理を利用者が意識しなくてもよいように隠蔽することです。今あなたが、パソコンやインターネットの仕組みを意識せずに利用しているのと同じですね。(違うかもしれません)
1-1-4. まとめ
さて、以上3つの「カプセル化」の説明を見てきましたが、これらは定義として、相互にまったく矛盾しています。一度ここで、3つの説明を整理してみます。
第一の主張は、「カプセル化」を「(中身を)まとめること」として説明します。
第二の主張は、「カプセル化」を「(中身を)まもること」として説明します。
第三の主張は、「カプセル化」を「(中身を)かくすこと」として説明します。
どれも確かに、「カプセル化」という言葉のイメージから容易に理解できる説明です。確かにカプセルはまとめるものでもあるし、中身をまもるものでもあるし、中身を隠すものでもあります。一応、整合的に理解できなくはない。
しかし問題は、何が「カプセル化」の定義で、その定義からどのような特徴が演繹されるのかという理路が全く示されていない点です。そのため、「カプセル化」の定義がずっと曖昧なままで、「カプセル化」の周辺的な特徴が列挙されているだけという状況になっています。
例えば数学で、三角関数の定義が全く示されないまま、三角関数にはこれこれの特徴があって、ほかにもこれこれの特徴があって、と説明されても理解できないでしょう。まずは定義があり、その定義から特徴が導出されるのです。特徴から定義が帰納されるわけではありません。
にもかかわらず、現状「カプセル化」という言葉は、統一的な定義が与えられていません。その具体的な側面が、いくつか列挙されている(だけ)、という状態です。書籍でさえそうなのですから、インターネット上の記事も考量すると、この混乱はもはやカオスのレベルです。
専門用語の定義くらいしっかりしてほしいものです。
なお、この点については以下の記事も参照してみてください。定義の混乱が2009年から起こっていることがわかり興味深いです。
[カプセル化、情報隠蔽、データ隠蔽] (https://bleis-tift.hatenablog.com/entry/20090201/1233426011)
1-1-5. 解決策
と言ってみても、この現状は変わりそうにありません。そこで私たちは、どの書籍の記述を信頼するのかを選択する必要があります。では、どれを信用するべきでしょう。
Jave SE Bronzeの試験を受けるにあたっては、やはり専門の参考書である黒本を信頼するのが安全だと思います。つまり「カプセル化」とは「(中身を)まとめること」であり、この定義から、「(中身を)まもること」と「(中身を)かくすこと」という二つの特徴が導出されるのだと理解するのです。
実際、「カプセル化」の定義をふわっと理解している状態で私は試験を受けましたが、特に損害はありませんでした(少なくとも私は)。おおよそ「カプセル化」という言葉が指す範囲をざっくりと理解していれば、問題はないかなというのが正直な実感です。(でも同時に、もっと厳密でいてほしいとも思いますが)
1-2. ポリモーフィズム
次に、「ポリモーフィズム」という言葉について検討します。これは、果たしてどのように定義されている用語なのでしょうか。
1-2-1.『徹底攻略 Java SE Bronze問題集[1Z0-818]対応』
黒本には、以下のように記述されています。
オブジェクト指向では、オブジェクトを抽象化して扱うことを「ポリモーフィズム」と呼びます。
すなわち例えば、「猫」「犬」「蛇」などのオブジェクトを「動物」という抽象的なクラスで扱うことが「ポリモーフィズム」だということです。「ポリモーフィズム」と「抽象化」はとても近い概念のようですね。本書においては、「抽象化はポリモーフィズムそのもの」という記述さえあります。本当にそうでしょうか?
1-2-2.『スッキリわかるJava入門 第3版』
というのも上の定義は、一般的に主張されているものだとは言えないのです。今度は、『スッキリわかるJava入門 第3版』のほうを見てみましょう。
このように、呼び出し側は相手を同一視し、同じように呼び出すのに、呼び出される側は、きちんと自分に決められた動きをする(同じ呼び出し方なのに、多数の異なる状態を生み出すことがある)という特性から「多態性」という名前が付けられています。
なお、「多態性」とは「ポリモーフィズム」の訳語です。本書で「ポリモーフィズム」の定義らしきものは、これのみだと思います。
この定義によれば、「ポリモーフィズム」と「抽象化」は、直接的には関係なさそうです。どちらかといえば、「具体化」のほうに関係がありそうに読めます。同じように呼び出しに、それぞれが別個の、個別具体的な動きで対応するということだからです。「オブジェクトを抽象化して扱う」というより、「オブジェクトを具体化して扱う」ですよね。
ですからこの記述の主張は、次のようにまとめられます――「ポリモーフィズムとは一つの抽象的なメソッドが複数の具体的な挙動をすることである」。簡単に言えば、「お菓子を買ってきて」という指令で、Aさんはドーナツを、Bさんはポテチを、Cさんはねるねるねるねを買ってくる、みたいな感じだと思います。
ところで、引用部分はそもそも定義として不十分だと思います。定義とは「AとはBである」というかたちで提示されるものです。(必要条件)
本書では、「多態性の定義は章の最後に紹介します」と述べています。その最後で示されたのが、先の引用部分の記述です。しかしこれは明らかに、「AとはBである」という形式になっていません。「これこれの特性があり、それが多態性だよ」という記述です。「Bのようなことができて、これがAである」という形式ですよね。ただ具体例を出すにとどまっていますよね。
「人に優しくしたくなる時があって、これが人間性だよ」と言われたとして、宇宙人のあなたは「人間性」の定義を得られるでしょうか?
1-1-3.『Java入門編第3版 ゼロからはじめるプログラミング』
三谷純先生の著書も見てみましょう。一体「ポリモーフィズム」とは、「抽象化」のことなのでしょうか。それとも「具体化」のことなのでしょうか。
ポリモーフィズムは多態性(多様な動作をする)という意味の言葉で、プログラミングの世界では、同じ型の変数に代入されたインスタンスに対して同じ名前のメソッドを呼び出しているのに、実際に参照されているインスタンスの種類によって異なる動作をすることをいいます。
この説明は、2番目の説明にとても近いと言えると思います。要するに、同じものが色んな動きをするよ、ということですよね。多数決原理主義者ならば、「ポリモーフィズム」とは「具体化のことだ」と理解するかもしれません。しかし、本当にそうでしょうか?
1-2-4. まとめ
さて、なんだか細かいところをついてきましたが、これに関しては整合的に理解できなくもありません。「抽象的に定義しているから、それを状況に応じて具体的に再定義できる」と理解すれば、「抽象化」も「具体化」も同じ「ポリモーフィズム」と言えるかもしれません。
しかしそれでもやはり、「何が定義か」ということが問題になります。「抽象化」が定義で、そこに「具体化」が付随するのでしょうか。それとも「具体化」が定義で、そこに「抽象化」が付随するのでしょうか。
先にも申しましたが、「何が定義か」を明記してほしいですよね。一体「ポリモーフィズム」とは何なのか。具体例に逃げるのでも、周辺的な特徴を列挙するのでもなくて。定義を示してほしい。
「ポリモーフィズム」という言葉は、「抽象化」のことなのか「具体化」のことなのか、それとも両者を束ねる上位概念なのか。厳密な人ほど混乱するだろうと思います。
1-2-5. オーバーロードはポリモーフィズムか
どうして私がこれほど神経質に「ポリモーフィズム」の定義を気にするかというと、やはり整合的に理解できない部分があるからです。
Javaにはオーバーロードという概念があります。これは引数(仮引数)の数や引数(仮引数)の型などが異なる同名のメソッドを複数定義し、与える引数(実引数)によって呼び出すメソッドを変えようという発想です。
class Call{
public static void call (){
System.out.println("田中くん!");
}
public static void call(String s){
System.out.println(s + "くん!");
}
}
以上のようにCallクラスを定義したとします。このとき、mainメソッドからは、以下の二種類の呼び出しができます。
public class Main{
public static void main(String[] args){
Call.call(); //田中くん!
Call.call("坂上"); //坂上くん!
}
}
このように、どちらもcallという同じ名前でありながら引数が異なるので、「田中くん!」を出力したり「坂上くん!」を出力したりと、まったく別の動きをするというのが、オーバーロードの具体例です。
ですから私には、オーバーロードは「ポリモーフィズム」の一種だとしか思えません。実際、そのように解説しているサイトもたくさんあります。
Javaのポリモーフィズムについて現役エンジニアが解説【初心者向け】
Java ポリモーフィズムとは
第9章 ポリモーフィズム
などなどです。
しかしここで、黒本の解説を参照してみましょう。
メソッドのオーバーロードとは、異なるシグネチャを持つ同名のメソッドを複数定義する「多重定義」のことで、ポリモーフィズムとは関係ありません。
なんと! オーバーロードは「ポリモーフィズム」とは関係ないそうです。上にあげたサイトに書かれていることは、偽なのでしょうか?
このように、用語の定義が混乱していると、まったく矛盾する正反対の意味の記述が簡単に提出されてしまいます。果たして、「オーバーロード」は「ポリモーフィズム」なのか、それとも「ポリモーフィズム」ではないのか。私たちには、まったく判断不可能です。
語りえぬものについては沈黙しなければなりませんが、しかし試験は受けなくてはなりません。いったい、何を信じればいいのでしょうか。
1-2-6. 解決策
さて、私たちは黒本の記述を参考にするべきでしょうか。それとも、その他の記述を参考にするべきでしょうか。
「ポリモーフィズム」は「抽象化」なのか「具体化」なのか。オーバーロードを含む概念なのか、含まない概念なのか。
ここでもやっぱり、黒本の記述をとりあえず信頼しておくのが無難だろうと思います。少なくとも試験を受けるにあたっては。オーバーロードはポリモーフィズムではないと理解するわけです。
オーバーロードはポリモーフィズムだと主張するサイトもあるし、僕もその理解で問題はないと思うし、実際僕に教えてくださった先生もそう仰っていたけれど、そうではないのです。オーバーロードはポリモーフィズムではない。なぜそう言えるのか? 黒本にそう書かれているからです! まるで聖書ですね。
2.説明の混乱
Java SE Bronze試験では、参考書として黒本を使うことになると思います。しかしこの黒本なのですが、解説がとても親切だとは言えません。矛盾しているように感じられる部分や、説明が足りない部分や、文章が成立していない部分があります。
2-1. エントリーポイントの条件
エントリーポイントとは、処理を始めるためのメソッドのことです。(黒本参照)
簡単に言えばmainメソッドのことだと思います。最初に呪文として覚えさせられるあれです。public static void main(String[] args)というあれですね。
このエントリーポイント、こうあるべきという条件がいくつかあります。黒本では、次のような条件があるとされています。
引数はString配列型を1つ受け取ること
要するに、mainメソッドの引数、String[] argsの部分はString型の配列だよということですね。だからString[]と書くべきであって、たとえばintとかbooleanとかはだめだよと主張しています。
では、次のようなコードはどうなるのでしょうか。
public class Main{
public static void main(Char[] args){
System.out.println(args[0]);
}
public static void main(String[] args){
System.out.println(args[1]);
}
}
mainメソッドの引数に、char[]と書かれています。つまり、char型の配列が指定されています。すなわち、String型の配列が指定されていません。つまり、エントリーポイントの条件を満たしていません。ですから、コンパイルエラーがでます――――そう思いませんか? 私はそう思いました。
しかしこのコード、コンパイルエラーになりません。String型の配列を指定しなければならない部分に、char型の配列を指定しても、エラーにはならないのです。
この現実と先の解説は、もう矛盾しているようにしか思えません。というか、解説が不十分だと思います。なぜchar型でも大丈夫なのでしょうか。
先にString型じゃないとダメといったのだから、論理の整合性は保ってほしい。ここで、黒本の該当部分の解説を見てみましょう。
設問のコードは、mainという名前のメソッドを2つ宣言しています。それぞれの引数の型が異なるため、この2つのメソッドはオーバーロードとして扱われます。よって、コンパイルエラーは起きません。
オーバーロードして扱われればコンパイルエラーにならないのなら、String型の配列を引数に持つmainメソッドを一つ用意しておけば、int型やboolean型の配列を引数に持つmainメソッドをいくらでも追加してよいということでしょうか。(ちなみに、追加してよいです。少なくともコンパイルエラーにはなりませんでした。私の環境では)
ですから、最初の記述は明らかに間違っています。不正確、不十分です。正確には、以下のような記述が正当だと思います。
エントリーポイントの条件の一つは、引数としてString配列型を1つ受け取るメソッドが少なくとも一つ宣言されており、したがって、ほかのメソッドをオーバーロードし得ることである。
2-2.(気力が失せて、以降省略します。また時間のあるときに、漸次追記しますね)
暫定的な結論
以上、Java SE Bronzeの試験範囲における矛盾点を概観しました。もっとも、その「矛盾点」というのは、私が矛盾していると感じる点、という程度の意味です。本当は矛盾していないのかもしれません。私の勉強不足で、矛盾しているように感じられるだけかもしれませんね。(というか、多分そうでしょうね)
しかし、おそらく私のように考える人も、一定数いるのではないかと愚考します。説明や定義に少しでも違和感を覚えると、「矛盾してるじゃないか」と敏感に反応してしまうような人です。この記事は、そういう方のために書きました。つまり、自分と同じような方のために書いたということです。
もしこの記事が、体系的・論理的な説明を求める人のための、簡単なガイド程度になれば幸いです。
以上です。お読みいただきありがとうございました。
補足
なお、ご指摘ご批判は大歓迎です。よろしくお願いいたします。
参考文献
・志賀澄人, 山岡敏夫『徹底攻略java SE Bronze 問題集 [1z0-818]対応』(株式会社インプレス, 2022)
・三谷純『Java 第3版 入門編 ゼロからはじめるプログラミング』(株式会社翔泳社, 2022)
・中山清喬, 国本大悟『スッキリわかるJava入門 第3版』(株式会社フレアリンク, 2023年)
・bleis-tift『カプセル化、情報隠蔽、データ隠蔽』https://bleis-tift.hatenablog.com/entry/20090201/1233426011(2024年4月23日 最終閲覧)