クラスについて
今回はクラスについてです。
まずは筆者のどうでもいい記憶について話。苦い記憶ではありますが、同時に学びにもなったpython学習中の経験を思い出しました。まだ本格的なプログラミング言語として初めて試行錯誤で取り組みだしたのがpythonでしたが、まだAIもなく、自分の欲のままにしたいことを実現するための処理を調べてはコードしてを繰り返していました。そしてあるとき関数を学び、関数すげーと思って、関数ばかり作って、その後も作りたいソフトをどんどん拡張させていきました。なんとなくクラスというのがあるけど、難しそうだし、今の知識でなんとかなってるからいいかなと思ってかなり後回しにして拡張をつづけていました。しかし、何かバグが発生してからいよいよ管理できなくなってきたときにいろいろ調べているとクラスを使うことで管理がしやすそうになりそうだったので、いよいよクラスを学んで実装しようと思ったら、ずいぶん無駄なメソッドと、ディレクトリ構造の悪さにフンギーと一時なってたのを思い出しました。
設計って本当大事。
さて本題ですが、pythonではメソッドだけで記述してつらつら増やしていけたのですが、どうやらjavaはメソッド単体で定義はできず、まずはクラスを作成して、その中にメソッドを作成するルールのようです。
ソースファイルの分割
さきほどのクラスを作成してからメソッドを作成というルール上、Mainクラスのなかに複数のメソッドがあり、それを分割してファイルごとに分けたいとなったとき、その分割ファイル内にまずはクラスを作成することから始めるようです。
- 別のファイルにクラスを作成
- 同ディレクトリに分割したクラスファイルがあれば同Mainクラス内でクラス名だけで呼び出すことが可能
複数クラスのコンパイル
あまりに記憶に残ってなかったけど、地味に便利なのでメモ
以下の例はCalc.javaとCalcLogic.javaの2つを同時にコンパイルできる
javac Calc.java CalcLogic.java
プログラムの実行方法
複数ファイルがある場合はプログラムのmainメソッドがあるクラスの名前がわからなければ起動できないので、プログラムを渡す相手にはmainメソッドがあるファイル名を伝えることが必要
JARファイル
配布時に複数のクラスファイルを1つにまとめるファイル形式として定められている。Java ARchiveの略称。
パッケージ
先生の説明ではグループ名であり、フォルダ名とのこと。教科書では各クラスをパッケージ(package)と呼ばれるグループに所属させて、分類・管理できるしくみとのこと。
実際これまで作成してきた勉強用のクラスにはpackage文を記述しておらず、どのパッケージにも属していない状態でした。これをデフォルトパッケージに属しているというらしい。このデフォルトパッケージはimport文でインポートすることはできないようです。逆にいえば、importして使うクラスを作成するようなある程度の規模のプログラムにはpackageを記述することが必須と考えてよさそうですね。
あと気づけたこととしてパッケージに親子関係や階層関係はないとのこと。これはいわゆる親パッケージをインポートしたら子パッケージの中のクラスもインポートできるようになってないのという疑問に解を与えてくれます。
ほかとどめておくべきこと
- パッケージの宣言はこのクラスがその中に帰属しているということ示す
- だから指定したディレクトリに入れておかないとプログラムからは見えない
- 別パッケージのクラスを利用するときは**完全限定クラス名(FQCN)**で呼び出す
- import パッケージ名.クラス名で使いたいクラスを先に指定しておくと、FQCNで記述せずとも対象のクラス名だけで使えるようになる
- import文はクラス名で書くこと。ワイルドカードを使ってそのディレクトリ内のクラス全てをインポートできるようになるが、できるだけクラス名で書くことを推奨されている
- パッケージ名は自由に命名してよいが、パッケージ名の衝突を避ける方法としてjavaはパッケージ名をドメインを逆順にすることを推奨している。これを逆ドメイン名という。
クラスローダー
クラスパスを基準としてパッケージ階層に従ったフォルダ構造を探し、読み込む
必要なクラスだけを、必要になったときにロードする。
一応geminiにも解説を頼んでみると独自(自作)のクラスローダーを作る方法という項目があり、興味をひかれたが、ほかにエネルギーを使うべきところがあるのでそのうち調べてみようと思う。
クラスパス
javaやjavac等の環境に対してクラスファイルの場所を教える
以下の例はクラスパスであるbinディレクトリにあるクラスMainを実行というコマンド
java -cpまたは-classpath bin Main
必要ならば環境変数で事前に指定しておくことも可能
指定がなければコマンドを実行したディレクトリがクラスパスとなる
またクラスパスにはjarファイルやzipファイルも指定できるようである。
複数のパスを指定でき、デリミタ文字で区切って指定できるとのこと。
※デリミタ文字→知らない言葉だったので、geminiに確認。
デリミタ文字とは、コンピュータのデータやテキストの中で、複数の項目や要素の区切りを示すために使われる記号や文字(区切り文字)のことです。
主なデリミタ文字の種類
- カンマ ( , ):CSVファイルなど、データをカンマで区切る場合に最もよく使われます。
- タブ ( \t ):TSVファイルなどで使われる、見えない空白の区切りです。
- スペース ( ):単語やデータの区切りとして使われます。
- セミコロン ( ; ):カンマを別の用途で使う欧州などの国で、データの区切りとして使われます。
- パイプ ( | ):データ内にカンマが含まれる場合など、区別をつけやすくするために使われます。
使用例(Windows)
c:\work;c:\work\jars\calcapp.jar
使用例(LinuxまたはmacOS)
/var/javadev:/var/javadev/jars/calcapp.jar
あとよくあるディレクトリ名としてsrcはsourceの略でソースファイルの配置場所を指し、binはbinaryの略で実行可能なプログラムを置くところとのこと。
パス
OSに対してプログラムの場所を教える。
基本的なことなんだろうけど、今まで感覚的に理解していたので改めて確認。
API
文脈からなんとなく理解していたAPIも改めて確認。
Application Programming Interfaceの略でAPIとのこと。ここでの話で言えば、javaに始めから存在するクラスのことを言うようである。APIのSystemクラスを呼び出すとき、java.langはしょっちゅう使うので、通常は明示的に記述する必要なし。詳しい使い方はAPIリファレンスhttps://docs.oracle.com/javase/jp/8/docs/api/ で公開されているので、必要に応じて調べる必要あり。
バージョンでよくあるLTSという言葉もここで学習。Long Time Supportの略であり、長期サポート版とのこと。
今回学んだjavacのコマンドオプション
- --class-path , -classpath , -cp
ユーザー・クラス・ファイルおよび注釈プロセッサを検索する位置を指定する - -d 生成されたクラス・ファイルを格納する位置を指定する
-cpのヘルプが分かりづらかったので、geminiにさらに質問。わかりやすく説明してくれました。以下geminiの解説
javac のヘルプにある --cp(または -classpath)は、プログラムのコンパイルに必要な外部の部品(ライブラリや他のプログラム)が「どこに置いてあるか」をコンパイラに教えてあげるための設定です。
初心者の方にも分かりやすいよう、言葉の意味と具体的な役割を噛み砕いて解説します。
- 「ユーザー・クラス・ファイル」を検索する位置とは?
Javaでプログラムを書くとき、自分が作った別のプログラムや、他人が作った便利なライブラリ(JARファイルなど)を呼び出して使うことがよくあります。
ユーザー・クラス・ファイル: 標準のJava(画面に文字を出す、計算するなど)以外の、あなたが作ったクラスファイル(.class)や、外部から持ってきたライブラリのことです。検索する位置を指定する: javac(コンパイラ)に対して、「このプログラムをコンパイルするときに必要な部品は、〇〇というフォルダ(またはJARファイル)の中に入っているから、そこを探してね」と保管場所(パス)を指示することを意味します。
もしこれを指定しないと、javac は「そんな部品は見つかりません」とエラーを出してコンパイルに失敗してしまいます。
2. 「注釈プロセッサ」を検索する位置とは?
Javaには、コードの中に @Override や @Deprecated、あるいは外部ライブラリ(Lombokなど)が提供する @Data のような @ から始まる記述(注釈 / アノテーション)があります。注釈プロセッサ(Annotation Processor): コンパイルするときに、それらのアノテーションを読み取って、自動で新しいコードを生成したりチェックしたりしてくれる裏方のプログラム(プラグインのようなもの)のことです。
裏方のプログラムもただのJavaのクラスファイルなので、javac に「注釈プロセッサの本体はここにあるよ」と教えてあげる必要があります。特別な指定(-processorpath)をしない限り、--cp で指定した場所から一緒に探してくれます。
💡 具体例で見る --cp のイメージ
例えば、あなたが MyProgram.java というプログラムを作ったとします。このプログラムの中では、別のフォルダ(libs フォルダ)の中にある Utility.class という便利な部品を使っています。
このとき、ただ javac MyProgram.java とだけ実行すると、コンパイラは Utility がどこにあるか分からずエラーになります。
そこで --cp を使って場所を教えてあげます。javac --cp ./libs MyProgram.javaこのように書くことで、「./libs フォルダの中を探せば、必要なユーザー・クラス・ファイル(部品)が見つかるから、それを使ってコンパイルしてね!」と javac に伝えることができます。