2019/02/06
sponsored link
奇麗なプログラム?
前回、時刻によって挨拶が変わる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>=18 && time<=24) || (time<=3 && time>=0)){ System.out.println("只今の時刻は" + time + "時です"); System.out.println("おやすみなさい"); }else{ System.out.println("timeには0~24の値を入力してください"); } } }
このコードをパッと見て思うのは、「System.out.println();」だらけだと言うことです。それに伴って日本語だらけです。しかも同じ言葉ばっかり。この辺をスリムにしていきます。
その為に、変数型Stringを使います。変数って何?で使ったintは数値(整数)を入れる変数型でした。Stringとはダブルクォーテーションで囲いさえすれば日本語でもなんでも文字列を入れることができる変数型です。
※Stringは変数型というより正しくはオブジェクトなのですが、まずは変数型の一つとしてStringの使い方を理解しましょう。
「只今の時刻は8時です」等の時刻を出力する文字列を入れるString型変数messageと、「おはようございます」等の挨拶の部分を入れるString型変数greetingをそれぞれ用意します。その変数にif文で分岐した先で「おはようございます」とか「こんにちは」とか時刻によって適当な挨拶をぶち込みます。それから最後に「System.out.println()」を使って、変数の中身を出力します。printlnメソッドの引数に直接文字列を渡すのではなく、文字列を入れた変数を渡すわけです。
プログラムのおおまかな流れはこんな感じです。
①変数の宣言&初期化 ↓ ②if文の中でそれぞれの時刻ごとに適した文字列(挨拶)を 変数greetingにぶち込む ↓ ③変数messageとgreetingを出力
こんな感じでやってみましょう。
①変数の宣言&初期化パート
まずはおさらいも兼ねて、変数の宣言です。これはどんな型の変数でも同じです。
「変数型 変数名;」の形です。
int time; String message; String greeting;
初期化も一緒にやってしまいます。intはとりあえず8で(相変わらず手打ち時計です)。
messageは、わざわざif文で枝分かれした先で代入しなくてもtimeによって勝手に違う値が入ってくれるのでif文の前に初期化しておきます。
greetingは時刻によって内容が変わるのでif文の中に任せましょう。ここでは宣言だけにしておきます。
String型変数に代入できるのは文字列なので、注意が必要です。文字列は「”」ダブルクォーテーションで囲わなければなりません。例えば変数messageに「メッセージ」という文字列を入れるなら、
message = "メッセージ";
となります。
message = メッセージ;
こんなことするとコンパイルエラーになりますのでご注意を。
では変数の初期化と宣言のパートです。
int time = 8; String message = "只今の時刻は" + time + "時です"; String greeting;
前回もさらっとやりましたが、messageの初期化のところ分かりますか?
String message = "只今の時刻は" + time + "時です"
「=」と「+」という演算子が並んでいますね。演算子って何?を思い出して下さい。まず大原則として、代入演算子の「=」よりも算術演算子「+」が優先されて先に評価されます。そして「+」が二つありますが、同じ優先度の演算子が並んでいる場合は算数と同じで左からです。
変数timeには8が入っているとして、この式が評価される過程を見てみましょう。(変数timeの中身が変わっても評価のされ方は変わりません。)
(参考:【Java】文字列と数値の足し算(連結))
つまり、この初期化の式は最終的にこうなります。
message = "只今の時刻は8時です"
文字列が連結された結果、「只今の時刻は8時です」という文字列をmessageに代入されます。
これで、変数の宣言と初期化のパートは終了です。
②if文パート
続いてif文の中身です。前回のコードではこのif文の中で「System.out.println()」を使って出力まで全てしていましたが、今回のスリムバージョンでは出力はせずにgreetingに挨拶をぶち込むだけです。
if((time >= 4) && (time <= 10)){ greeting = "おはようございます"; }else if((time >= 11) && (time <= 17)){ greeting = "こんにちは"; }else if(time == 18){ greeting = "こんばんは"; }else if((time>=18 && time<=24) || (time<=3 && time>=0)){ greeting = "おやすみなさい"; }else{ message = "timeには0~24の値を入力してください"; }
こうすることでif文の中がかなりすっきりしますよね。何の為のif文かが一目瞭然です(出力内容を指定する為のif文)。
ただ、一つ気をつけてもらいたいのは、最後の「else」のところ、ここはtimeに0~24以外の変な数値を入れられた場合の警告文を代入しているのですが、「greeting」にではなく、「message」に代入しています。「message」には宣言&初期化パートで既に「只今の時刻は8時です」と代入していますが、それを上書きしました。もちろん変数の中身はintであろうとStringであろうと何度でも書き換えることができます。
なぜ「greeting」ではなくわざわざ「message」に代入しているかというと「timeには0~24の値を入力してください」という警告文を、挨拶を入れる為の変数「greeting」に入れてしまうのはどうも不細工な気がしたから、っていうのと他のやり方を考えた時、このやり方が一番すっきりするかなと思ったからです。
余力のある方は、同じ出力結果が得られるプログラムで、もっとすっきりしたコードが書けないか考えてみて下さい(他のパターンをいろいろ考えるだけでもコーディング力アップ間違いなしです)。
③出力パート
続いて、変数の中身を出力するパートです。
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>=18 && time<=24) || (time<=3 && time>=0)){ greeting = "おやすみなさい"; }else{ message = "timeには0~24の値を入力してください"; } System.out.println(message); System.out.println(greeting); } }
どうでしょうか?最初のコードと比べるとちょっと見やすくなったと思いませんか?ではこのすっきりしたコードで本当に出力結果が同じかどうかさっそく動かしてみましょう。
おおーっと!コンパイルエラー発生!
greetingが初期化されていない可能性があるとのこと!
実は、変数を宣言だけして初期化しないまま、つまり何も代入しないままその変数を使おうとすると、このコンパイルエラーメッセージが出ます。
greetingにはif文の中でそれぞれの時刻に合わせて文字列を代入するので、宣言&初期化パートではあえて初期化しませんでしたね。timeが0~24の間ならそれぞれ挨拶が代入されるからいいのですが、問題はtimeに0~24以外の変な数字を入れられてelseのブロックの中が実行された時です。elseブロックの中は、
}else{
message = "timeには0~24の値を入力してください";
}
と、messageに文字列を代入しているだけで、greetingには何も入れてません。
ここが「初期化されていない可能性」というやつです。
けどtimeには8が入っているからelseブロックが実行されることはないからいいじゃないか!と思うのも分かりますが、その論理はコンパイラには通用しません。if文のような分岐構造がある場合は、どのルートを通ってもプログラムに問題が無い状態になっていないとコンパイルは通してもらえません。非常に厳しいですが、こればっかりは言う通りにする他ありませんので、そういうルールは逐一覚えましょう。
で、どうしたらいいのか?ということですが、一番スマートな方法は、宣言&初期化パートでgreetingも初期化しておくことです。けど何を入れればいいか悩みますよね。もし「おはようございます」と入れていたら、elseブロックが実行された時の出力が、「timeには0~24の値を入力してください おはようございます」なんていうおかしな出力になりますから。
こういう場合は、空っぽの文字列で初期化しておくんです。
String greeting = "";
空っぽでもちゃんと初期化したことになります。「空っぽの文字列」と判定してくれるんです。String変数を宣言だけして初期化していない状態では「null」と言って本当の空っぽ(変な表現ですが)の状態です。「null」は文字列でも何でもなくただの「空っぽ」です。「無」です。「””」は「空っぽの文字列」です。この違いはプログラミングをする上では大きな違いなので、「null」も覚えていてください。
というわけで、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>=18 && time<=24) || (time<=3 && time>=0)){ greeting = "おやすみなさい"; }else{ message = "timeには0~24の値を入力してください"; } //③出力パート System.out.println(message); System.out.println(greeting); } }
ぜひ、timeの値をいろいろ変えて実行してみて下さい。コードは変わりましたが、前回と同じように手打ち時計に反応して挨拶を返してくれるはずです。
そうそう、「①変数の宣言&初期化」とか書いてますが、その左側に「//」っていうのが付いてますよね?これはコメントアウトと言って、プログラムには影響しないメモとかコメントを書き込む為に使います。
「//」の右側はコンパイル時には基本的には無視されるので何を書いてもOKです。「//」はその一行だけにしか効きませんが、「/*」と「*/」で囲んでやると複数行でも効きます。
これが地味にすごい役に立ちます。別にコメントをいっぱい書き込むわけじゃなく、プログラムが思ったとおりに動かなかったりエラーになったりして、どこに原因があるのか探るような時に、問題のありそうな箇所を消したり書いたりしてたら大混乱を招くこと間違いないので、プログラムは消さずにこのコメントマークで囲ってやるんです。そしたら消していないのに消えたことになってくれるので、コードを書き換えずにいろいろ試してエラーの原因を見つけることができます。
僕はアプリを作る時なんか知識も経験も何もなく試行錯誤の連続だったので、このコメントマークは本当に役立ちました。初めはそんなやり方も分からず「ここをこう書き換えたら上手く行くかも」なんて考えながら書いたり消したり試行錯誤しているうちにコードが滅茶苦茶になって何が何やら分からなくなって一から作り直したりもしました。。
ぜひ、自分でプログラムを書く際はこのコメントマークを使って試行錯誤する方法をお忘れなく。コードを安易にいじり倒さないようにして下さい。
次回は、配列というのを使って、また新しいclassを作っていきましょう。
今だけ→転職できなければ全額返金の「エンジニア転職保証コース」
絶対エンジニアになる!→テックエキスパート
フリーランスエンジニアの収入例を見てみる→レバテックフリーランス
コメント
②if文パートの 省略~ (time=0)
)が一つ多いようです
by 匿名 2015/07/13 11:21
細かいですが、一応。「コードすっきり計画」のコード「Greeting.java」の下、11行目の「System.out.println()」が「System.our.println()」になってます。
by 名無し 2015/07/13 19:55
>名無しさん
ありがとうございます。修正しました。
>匿名さん
確認できませんでした。。
もう少し絞って指摘していただけると助かります。ありがとうございます。
by Nobuo@管理人 2015/07/13 20:23
②if文パートのコード7行目のif文のカッコの位置がおかしいです。
by 名無し 2015/07/13 23:43
③番の
}else if((time>=18 && time<=24) || (time=0)){
greeting = “おやすみなさい”;
と
②番の
}else if(time>=18 && time<=24) || (time=0){
greeting = “おやすみなさい”;
だとtimeの前の括弧と0のあとの括弧の数が違うのですが・・・
by daiki 2015/08/05 14:18
コードの前に空白をつけていますがいみはあるのですか?
else if
↑みたいなのです
by daiki 2015/08/05 14:29
ご指摘下さった方、ありがとうございます。
カッコの数があってなかったのを修正しました。。
>daikiさん
空白というのはどこでしょうか?字下げのことでしたら、プログラム上は意味はありません。見やすいように正しく字下げする事は重要ですが。
by Nobuo@管理人 2015/08/05 15:43
コードすっきり計画の項目の「変数って何?」のリンク先が「演算子って何?」になっています
by 匿名 2016/03/18 01:53
ご報告感謝です。ありがとうございます。
修正しました。
by Nobuo@管理人 2016/03/18 08:56
コメント失礼します。
とても解りやすい説明ありがとうございます。
まだ、途中ですがすべて見させていただきますので、
よろしくお願い致します。
また、気づいた点なのですが、
項目「①変数の宣言&初期化パート」内の「演算子って何?」へのリンクが違うようです。
よろしくお願い致します。
by naru 2016/03/22 12:31