179.『Java 入門』プログラムの書き方
初回:2021/9/15
P子「いまさら、Hello World!なの?」(※1)
その通りです。
実際のところ、私には何が正解か分かりません。というのは、まったくの初心者でもないので『初心者が判らない箇所が判らない』からです。
ただ、Python を初め、各種言語を学習しようとするときに、最初に出くわす『Hello World』が、何かの役に立ったことがありません。その後学習を進めていくにつれて、毎回毎回、壁にぶつかって、実用的なプログラミングを行うまでにいくつもの苦難を乗り越える必要がありました。
P子「大げさね」
ここで言いたいのは、どのような入門が良いのか?という事です。
1.『入門』の目的
その前に、入門の目的を明確にしておきましょう。新しい言語を学習しようと思うきっかけは、色々とあります。そこで、まず実行環境を構築し、雰囲気を掴むために『Hello World』を動かすという手法がありますが、それは環境が正しく構築できたかどうかを確認するのが『目的』であって、言語の習得には関係ありません。
P子「そんなこと、言い切って大丈夫?」
単なる味見、料理で言うと、スプーンでスープを一口すする程度で、それでは本当の料理の味も良さも判りません。それに、C言語で書かれた当時の『Hello World』と、Javaのようにオブジェクト指向やパッケージなどの概念がある言語では、一般に示されている『Hello World』では、味見にもならないというのが、私の感想です。
皆さんが新しい言語に『入門』する目的は、単なる新しもの好きが知ったかぶりするために学習するのではなく、その言語を使いこなしたいという目的の元、入門するんだと思います。つまり、入門した後も、引き続き学習を続けるわけで、形だけの役に立たない入門プログラムなんて、役に立たない...というのが
P子「役に立たないがかぶってるわよ」
それくらい役に立たないという事です。
2.従来の『Hello World』
まずは、従来からある『Hello World』を作成して、コンパイル&実行してみましょう。(Javaのパスは通っていることとします)
Test1.java
class Test1{
public static void main(String[] args){
System.out.println("Hello World!");
}
}
> javac *.java
> java Test1
Hello World!
さて、これの問題点って判りますか?とりあえず、PMD(※2)を掛けてみましょう。
Test1.java:1: NoPackage: All classes, interfaces, enums and annotations must belong to a named package Test1.java:1: UseUtilityClass: All methods are static. Consider using a utility class instead. Alternatively, you could add a private constructor or make the class abstract to silence this warning. Test1.java:1: CommentRequired: Class comments are required Test1.java:2: MethodArgumentCouldBeFinal: Parameter 'args' is not assigned and could be declared final Test1.java:2: CommentRequired: Public method and constructor comments are required Test1.java:3: SystemPrintln: System.out.println is used
パッケージを使っていない、スタティックメソッドのみ、コメントが書かれていない、args変数が未使用である、System.out.printlnを使っている...というのが警告の内容です。
P子「args変数が未使用なのは、mainメソッドが特殊だから仕方ないかも」
System.out.printlnも、ここでは仕方がないと思っています。System.out.printlnは通常はデバッグ用に使うため、警告になっています。本番環境ではロギングクラスを使って、出力の抑制などの処理を外部から制御できるようにするためです。
さらに、ここには出ていませんが、コンパイルと実行についての説明が不足しています。Javaではソースもクラスもパッケージ管理します。ソースをパッケージに沿ったディレクトリ階層に配置し、コンパイルする時に、classファイルの出力先をソースと分離します。また、複数のclassファイルを jar 化する場合の書き方も不明です。
つまり、初心者が『Hello World』を学んで、本格的に使いだすには、色々な事を『プラスしていく』必要があり、その都度検索、試行錯誤を繰り返すことになります。
3.これからの『Hello World』
では、どのような『Hello World』プログラムを書くべきか、理想形を考えてみたいと思います。という事で、サンプルを作ってみました。
src\org\opengion\tools\Test2.java
/**
* Test2 クラスです。
*/
public class Test2 implements Comparable<Test2> {
/** 固定の文字列 {@value} */
public static final String HELLO_WORLD = "Hello World!";
/** 内部に設定するメッセージ */
private final String message ;
/**
* デフォルトコンストラクター
*/
public Test2() {
this( HELLO_WORLD );
}
/**
* 引数付きコンストラクター
*
* @param msg 内部に設定するメッセージ
*/
public Test2( final String msg ) {
message = msg == null ? HELLO_WORLD : msg ;
}
/**
* 内部に設定されたメッセージを表示します。
*/
public String getMessage() {
return message;
}
/**
* ハッシュコードを返します。
*
* @return このクラスのインスタンスのハッシュ値
*/
@Override
public int compareTo( final Test2 other ) {
return this.message.compareTo( other.message );
}
/**
* このオブジェクトの文字列表現が返されます。
*
* ここでは、内部に設定されたメッセージです。
*
* @return 文字列表現
*/
@Override
public String toString() {
return message;
}
/**
* このクラスのインスタンスと引数で渡されたオブジェクトが
* 同値である場合trueを返します。
* 引数で渡されたオブジェクトがTest2クラスのインスタンスで
* 内部に設定されたメッセージが等しい場合、同値であるとみなされます。
*
* @return 引数で渡されたオブジェクトが同値である場合、true。
*/
@Override
public boolean equals( final Object other ) {
// 出口を一つにすると言うPMD対策(普段はこんなことしないけど...)
return other == this || other instanceof Test2 && this.message.equals( ((Test2)other).message ) ;
// if( other == this ) { return true; }
// if( other instanceof Test2 ) { // other が null なら false
// final Test2 obj2 = (Test2)other;
// return this.message.equals( obj2.message );
// }
// return false;
}
/**
* ハッシュコードを返します。
*
* @return このクラスのインスタンスのハッシュ値
*/
@Override
public int hashCode() {
return message.hashCode();
}
/**
* 引数の第一パラメータを表示します。
*
* 引数を指定しない場合は、固定の文字列を表示します。
*
* @param args 引数配列
*/
public static void main( final String[] args ) {
final Test2 test2 ;
if( args.length == 0 ) {
test2 = new Test2();
}
else {
test2 = new Test2( args[0] );
}
final Test3 test3 = new Test3();
System.out.println( test2.equals(test3) );
System.out.println( test2.hashCode() );
System.out.println( test3.hashCode() );
System.out.println( test2.compareTo(test3) );
System.out.println( test2.getMessage() );
}
}
/**
* Test3 検証用クラスです。
*/
class Test3 extends Test2 {
/**
* デフォルトコンストラクター
*/
public Test3() {
super();
}
}
> javac -Xlint:all -encoding UTF-8 -d classes -sourcepath src src/org/opengion/tools/*.java
> java org.opengion.tools.Test2 "Hello World!"
true
-969099747
-969099747
0
Hello World!
> jar cvfe test.jar org.opengion.tools.Test2 -C classes org
マニフェストが追加されました
org/を追加中です(入=0)(出=0)(0%格納されました)
org/opengion/を追加中です(入=0)(出=0)(0%格納されました)
org/opengion/tools/を追加中です(入=0)(出=0)(0%格納されました)
org/opengion/tools/Test2.classを追加中です(入=1579)(出=839)(46%収縮されました)
org/opengion/tools/Test3.classを追加中です(入=215)(出=167)(22%収縮されました)
> java -jar test.jar "Hello World!"
true
-969099747
-969099747
0
Hello World!
P子「こんな長いプログラムなんて、おなか一杯になるわよ」
やっぱり。
でも、JavaソースをUTF-8でセーブして、パッケージを作成して、オブジェクトを生成して、mainから引数を渡してるし、static finalの固定値とか、thisやsuper、コンストラクターのオーバーロード、継承、インターフェース、それに、equalsやhashCodeのオーバーライドなども入れています。
P子「入門じゃなくって、どっぷりでしょ」
何が言いたいかというと、最初に出来るだけ本番に近いコードを学習して、いらない所を『マイナスしていく』方が、判りやすいと思います。
さらに、コンパイル(javac)や、実行(java)も、ソースとクラスフォルダの分離、jarでのマニフェスト登録、実行なども評価できます。
P子「Eclipse(エクリプス)とかの統合開発環境使えばいいじゃん」
実は、私としては『入門』時には、そういうツールを使うのではなく、基本が分かるように標準環境のみで実行するのが良いと思っています。
P子「ソースは本番を意識して、開発環境は本番環境に合わせないの?」
開発環境や、開発ツールは会社や部署によって異なるかもしれませんが、javaやjavacの考え方は変わりません。そして、javaのソースコードの基本は変わらないでしょう。『入門』時に学習すべきは、本物の基本であって、表面的なコードを動かして満足していてはいけないと思います。
P子「でも、精神的にやる気がなくなっちゃうんじゃない?」
この程度でくじけるようじゃ、本格的な開発なんて出来ないでしょう。つまり『入門』出来ないのなら、門前払いってことです。単純な『Hello World』を動かしただけで満足するより、良いと思います。
ほな、さいなら。
======= <<注釈>>=======
※1 P子「いまさら、Hello World!なの?」
P子とは、私があこがれているツンデレPythonの仮想女性の心の声です。
※2 PMD https://pmd.github.io/
Javaソースコードを解析するツールです。
スピンオフ:CIA京都支店『妖精の杜』
ここはCIA京都支店のデバイス開発室。安らぎを求めて傷ついた戦士が立ち寄る憩いの場所、通称『妖精の杜』と呼ばれていた。
P子:CIA京都支店の優秀なスパイ。早坂さんにはなぜか毒を吐く。
早坂:デバイス開発室室長代理。みんなから『妖精さん』と呼ばれている。
P子:「初心者にあんな難しいのは無理でしょ」
早坂:「僕は、初心者だからこそ...だと思うね」
P子:「どういう事?」
早坂:「最初に中途半端な事を覚えると後々引きずるんだよね」
早坂:「それに次に進むために、色々と追加で調べることになるし」
P子:「まあ、どこまで本気で取り組むつもりかによるかもね」
早坂:「本気じゃないなら、難しくって出来なくてもいいでしょ」
P子:「簡単なサンプルで判ったつもりになるだけでも有りってもんじゃない?」