文字列を保持するString
前回、時刻によって挨拶が変わるGreetingクラスを作りました。一応、思った通りに出力される状態に完成したのですが、今回はそれを改造して、もっとコード(プログラム)をスマートにしたいと思います。
綺麗なプログラム?
よく綺麗なプログラムとか、美しいプログラムとかっていうのを聞きますよね。綺麗なプログラムというのは、不要な繰り返しや重複がなくて必要最小限のスリムな状態のコードのことを言うんだと思います。
プログラムというのは実行結果は全く同じでも書きようによっては短くすることもできます。何も短けりゃいいってもんでもないですが、やっぱり余計なもんは余計です。なんか「うるさい」感じです。
例え思い通りに動いたとしても余計な部分がいっぱいあるようなプログラムは、もし後から修正しようと思った時にどこをいじればいいのか分からなくなったりします(実体験・・)。
なので、綺麗とか美しいとかっていうレベルにまで行かなくとも、極力、見やすいコードを書くように心がける必要があります。
コードすっきり計画
前回書いたのはこんなコードでした。
Greeting.java
public class Greeting {
public static void main(String[] args) {
int time = 8;
if ((time >= 4) && (time <= 10)) {
System.out.println("只今の時刻は" + time + "時です");
System.out.println("おはようございます");
} else if ((time >= 11) && (time <= 17)) {
System.out.println("只今の時刻は" + time + "時です");
System.out.println("こんにちは");
} else if (time == 18) {
System.out.println("只今の時刻は" + time + "時です");
System.out.println("こんばんは");
} else if ((time >= 19 && time <= 23) || (time <= 3 && time >= 0)) {
System.out.println("只今の時刻は" + time + "時です");
System.out.println("おやすみなさい");
} else {
System.out.println("timeには0~23の値を入力してください");
}
}
}このコードをパッと見て思うのは、「System.out.println();」だらけだと言うことです。それに伴って日本語だらけです。しかも同じ言葉ばっかり。この辺をスリムにしていきます。
その為に、変数型Stringを使います。変数って何?で使ったintは数値(整数)を入れる変数型でした。Stringは文字列を扱う型です。日本語などの文字列を代入するときは、ダブルクォーテーションで囲んで書きます。
補足:Stringはクラスです。Stringは変数の型として使えるクラスで、基本データ型のintとは違い「参照型」に分類されます。文字列そのものはStringクラスのオブジェクトです。まずは、文字列を扱う型としてStringの使い方を理解しましょう。
「只今の時刻は8時です」等の時刻を出力する文字列を入れるString型変数messageと、「おはようございます」等の挨拶の部分を入れるString型変数greetingをそれぞれ用意します。その変数にif文で分岐した先で「おはようございます」とか「こんにちは」とか時刻によって適当な挨拶をぶち込みます。それから最後に「System.out.println()」を使って、変数の中身を出力します。printlnメソッドの引数に直接文字列を渡すのではなく、文字列を入れた変数を渡すわけです。
プログラムのおおまかな流れはこんな感じです。
- 変数の宣言&初期化
- if文の中でそれぞれの時刻ごとに適した文字列(挨拶)を変数greetingにぶち込む
- 変数messageとgreetingを出力
こんな感じでやってみましょう。
①変数の宣言&初期化パート
まずはおさらいも兼ねて、変数の宣言です。これはどんな型の変数でも同じです。
「型 変数名;」の形です。
Greeting.java
int time;
String message;
String greeting;初期化も一緒にやってしまいます。intはとりあえず8で(相変わらず手打ち時計です)。
messageは、わざわざif文で枝分かれした先で代入しなくても、初期化する時点のtimeを使って文字列を作れるのでif文の前に初期化しておきます。
greetingは時刻によって内容が変わるのでif文の中に任せましょう。ここでは宣言だけにしておきます。
String型変数に代入できるのは文字列なので、注意が必要です。文字列は「”」ダブルクォーテーションで囲わなければなりません。例えば変数messageに「メッセージ」という文字列を入れるなら、
Greeting.java
message = "メッセージ";となります。
Greeting.java
message = メッセージ;この例では「メッセージ」という名前の変数を宣言していないので、コンパイルエラーになります。文字列そのものを書くときは、ダブルクォーテーションを忘れないようにご注意を。
では変数の初期化と宣言のパートです。
Greeting.java
int time = 8;
String message = "只今の時刻は" + time + "時です";
String greeting;前回もさらっとやりましたが、messageの初期化のところ分かりますか?
Greeting.java
String message = "只今の時刻は" + time + "時です";「=」と「+」という演算子が並んでいますね。演算子って何?を思い出して下さい。まず大原則として、代入演算子の「=」よりも文字列を連結する「+」が優先されて先に評価されます。そして「+」が二つありますが、同じ優先度の演算子が並んでいる場合は算数と同じで左からです。
変数timeには8が入っているとして、この式が評価される過程を見てみましょう。(変数timeの中身が変わっても評価のされ方は変わりません。)
- "只今の時刻は" + 8 + "時です"左の + で文字列と数値を連結
- "只今の時刻は8" + "時です"次の + で文字列を連結
- "只今の時刻は8時です"できた文字列をmessageに代入
- message = "只今の時刻は8時です"
(参考:【Java】文字列と数値の足し算(連結))
つまり、この初期化の式は最終的にこうなります。
Greeting.java
message = "只今の時刻は8時です";文字列が連結された結果、「只今の時刻は8時です」という文字列がmessageに代入されます。
これで、変数の宣言と初期化のパートは終了です。
②if文パート
続いてif文の中身です。前回のコードではこのif文の中で「System.out.println()」を使って出力まで全てしていましたが、今回のスリムバージョンでは出力はせずにgreetingに挨拶をぶち込むだけです。
Greeting.java
if ((time >= 4) && (time <= 10)) {
greeting = "おはようございます";
} else if ((time >= 11) && (time <= 17)) {
greeting = "こんにちは";
} else if (time == 18) {
greeting = "こんばんは";
} else if ((time >= 19 && time <= 23) || (time <= 3 && time >= 0)) {
greeting = "おやすみなさい";
} else {
message = "timeには0~23の値を入力してください";
}こうすることでif文の中がかなりすっきりしますよね。何の為のif文かが一目瞭然です(出力内容を指定する為のif文)。
ただ、一つ気をつけてもらいたいのは、最後の「else」のところ、ここはtimeに0~23以外の変な数値を入れられた場合の警告文を代入しているのですが、「greeting」にではなく、「message」に代入しています。「message」には宣言&初期化パートで既に「只今の時刻は8時です」と代入していますが、それを上書きしました。もちろん変数の中身はintであろうとStringであろうと何度でも書き換えることができます。
なぜ「greeting」ではなくわざわざ「message」に代入しているかというと「timeには0~23の値を入力してください」という警告文を、挨拶を入れる為の変数「greeting」に入れてしまうのはどうも不細工な気がしたから、っていうのと他のやり方を考えた時、このやり方が一番すっきりするかなと思ったからです。
余力のある方は、同じ出力結果が得られるプログラムで、もっとすっきりしたコードが書けないか考えてみて下さい(他のパターンをいろいろ考えるだけでもコーディング力アップ間違いなしです)。
③出力パート
続いて、変数の中身を出力するパートです。
Greeting.java
System.out.println(message);
System.out.println(greeting);これだけですね。printlnメソッドに、直接文字列を渡すのではなく文字列を保持した変数を渡しています。これで変数の中身の文字列が出力されるわけですね。
さて、出来上がりました。スリムになったコードを見てください。
Greeting.java
public class Greeting {
public static void main(String[] args) {
int time = 8;
String message = "只今の時刻は" + time + "時です";
String greeting;
if ((time >= 4) && (time <= 10)) {
greeting = "おはようございます";
} else if ((time >= 11) && (time <= 17)) {
greeting = "こんにちは";
} else if (time == 18) {
greeting = "こんばんは";
} else if ((time >= 19 && time <= 23) || (time <= 3 && time >= 0)) {
greeting = "おやすみなさい";
} else {
message = "timeには0~23の値を入力してください";
}
System.out.println(message);
System.out.println(greeting);
}
}どうでしょうか?最初のコードと比べるとちょっと見やすくなったと思いませんか?ではこのすっきりしたコードで本当に出力結果が同じかどうかさっそく動かしてみましょう。
error: variable greeting might not have been initializedgreetingが初期化されていない可能性があります。表示言語や実行環境により、エラーメッセージの表記は異なります。
おおーっと!コンパイルエラー発生!
greetingが初期化されていない可能性があるとのこと!
実は、変数を宣言だけして初期化しないまま、つまり何も代入しないままその変数を使おうとすると、このコンパイルエラーメッセージが出ます。
greetingにはif文の中でそれぞれの時刻に合わせて文字列を代入するので、宣言&初期化パートではあえて初期化しませんでしたね。timeが0~23の間ならそれぞれ挨拶が代入されるからいいのですが、問題はtimeに0~23以外の変な数字を入れられてelseのブロックの中が実行された時です。elseブロックの中は、
Greeting.java
} else {
message = "timeには0~23の値を入力してください";
}と、messageに文字列を代入しているだけで、greetingには何も入れてません。
ここが「初期化されていない可能性」というやつです。
けどtimeには8が入っているからelseブロックが実行されることはないからいいじゃないか!と思うのも分かりますが、その論理はコンパイラには通用しません。if文のような分岐構造がある場合は、どのルートを通ってもプログラムに問題が無い状態になっていないとコンパイルは通してもらえません。非常に厳しいですが、こればっかりは言う通りにする他ありませんので、そういうルールは逐一覚えましょう。
で、どうしたらいいのか?ということですが、一番スマートな方法は、宣言&初期化パートでgreetingも初期化しておくことです。けど何を入れればいいか悩みますよね。もし「おはようございます」と入れていたら、elseブロックが実行された時の出力が、「timeには0~23の値を入力してください おはようございます」なんていうおかしな出力になりますから。
こういう場合は、空っぽの文字列で初期化しておくんです。
Greeting.java
String greeting = "";空文字でもちゃんと文字列として初期化したことになります。つまり"" は、文字数は0だけど文字列だと認識してくれるんです。
というわけで、Greetingクラスのスリムバージョンの完成です。
Greeting.java
public class Greeting {
public static void main(String[] args) {
//①変数の宣言&初期化パート
int time = 8;
String message = "只今の時刻は" + time + "時です";
String greeting = "";
//②if文パート
if ((time >= 4) && (time <= 10)) {
greeting = "おはようございます";
} else if ((time >= 11) && (time <= 17)) {
greeting = "こんにちは";
} else if (time == 18) {
greeting = "こんばんは";
} else if ((time >= 19 && time <= 23) || (time <= 3 && time >= 0)) {
greeting = "おやすみなさい";
} else {
message = "timeには0~23の値を入力してください";
}
//③出力パート
System.out.println(message);
System.out.println(greeting);
}
}ぜひ、timeの値をいろいろ変えて実行してみて下さい。コードは変わりましたが、前回と同じように手打ち時計に反応して挨拶を返してくれるはずです。
そうそう、「①変数の宣言&初期化」とか書いてますが、その左側に「//」っていうのが付いてますよね?これは行コメントの書き方で、プログラムには影響しないメモとかコメントを書き込む為に使います。
「//」の右からその行末まではコメントとして扱われ、ソースコードとしては何も書いていないことになります。「//」はその一行だけにしか効きませんが、「/*」と「*/」で囲んでやると複数行でも効きます。こうやってソースコード内の文字を実行対象から外すことを「コメントアウト」と言います。
次回は、配列というのを使って、また新しいclassを作っていきましょう。
質問・コメント
読み込み中この記事についての質問や、学習して気づいたことをお寄せください。
この記事のコメント
旧サイトに寄せられたコメントも当時の投稿内容のまま掲載しています。現在の記事では変更している内容や、旧環境についてのやり取りも含まれます。
コメントを読み込んでいます。