Q & A
- フォーマット記述子とは何ですか
- なぜ\nではなく,%nを利用するのでしょうか
import文とは何ですか- 可視性とは何ですか
- リンクリストとは何ですか
- インデントが面倒です
- 変数のスコープが短い方が良いのはなぜですか?
- なぜmainメソッドに処理を書かない方が良いのでしょうか
- キャメルケースって何ですか?
- System.exit は使わない方が良い?
- ヨーダ記法は避けるべき?
- なぜメソッドに分けないといけないのでしょうか
- どのようにメソッドに分ければ良いのでしょうか
- インデントを揃えているのにインデントがズレていると指摘を受けました
- Javaのソースコードはどこで見られますか
- 講義資料に間違いを見つけました
フォーマット記述子とは何ですか
System.out.printf に渡す%sなどの表示形式を指定するための文字列です.
Java言語の場合,C言語とほぼ同じですが,Javaの場合は型がより多彩ですので,どの記述子にすべきかを注意する必要があります.
記述子が対応しない型の値を対応づけると実行時エラー(IllegalFormatConversionException)が発生します.
フォーマット記述子と対応する型を以下に示します.
%c- 1文字(characterの頭文字)を出力する.
- 対応する型:
Character型
%s- 与えられた値の文字列表現(stringの頭文字)を出力する.
- 対応する型:何でもOK.
%d- 整数を10進数で出力する(decimalの頭文字).
- 対応する型:
Integer型,Long型,Short型
%o- 整数を8進数で出力する(octalの頭文字).
- 対応する型:
Integer型,Long型,Short型
%x- 整数を16進数で出力する(hexadecimalのx).
- 対応する型:
Integer型,Long型,Short型
%f- 浮動小数点変数を出力する(floating point numberの頭文字)
- 対応する型:
Float型,Double型
%e- 浮動小数点変数を指数表現で出力する(exponentialの頭文字).
- 対応する型:
Float型,Double型
%n- 改行を出力する.実行環境によって改行コードが異なり,その違いを吸収するため.
なぜ\nではなく,%nを利用するのでしょうか
C言語では,\nで改行を表していましたが,Javaでは明示的に \n を使うことはありません.
改行のみを出力するときは,System.out.println(); とし,
改行付きで出力するときは,printlnメソッドの引数に出力したい内容を渡します.
また,System.out.printf でも,\nは使わず%n を利用する方が良いとされています.
なぜなら,改行コードはプラットフォーム(WindowsやmacOSなど)で異なるためです.
Windows では,改行は\r\nの2バイトで表されており,macOSやLinuxは\nで改行を表しています.
古いmacOS(MacOS 9以前)は\rで改行を表していました.
一方で,Java は,一度書けばどこでも動く(Write Once, Run Anywhere)ことを重要視しています. そのため,環境ごとの違いをどこかで吸収する必要があります. macOSでは改行されるのに,Windowsでは改行されない,のようなプログラムがあっては困るわけです. そのため,改行コードを直に書くことは避け,改行コードを表す記号を利用する方が良いとされているわけです.
改行が必要な場合は,printlnや printfに%nを渡すようにしましょう.
import 文とは何ですか
Javaの型は必ずパッケージに所属しています.
パッケージとは,ディレクトリのようなもので,階層構造が存在します.
パッケージには,サブパッケージと型が属します.
型が約4,000個存在するため,パッケージを導入して分類しなければ混乱するためです.
標準的には,java.langパッケージに所属する型が利用できます.
しかし,java.langパッケージに所属する型以外を利用する場合は,どのパッケージの型を利用するのかを指定しなければいけません.
その指定を行うのが,import文です.
Java言語の型一覧は次のURLから確認できます. https://docs.oracle.com/javase/jp/8/docs/api/
なお,java.langパッケージには,String型やInteger型,Double型,
System 型などが所属しています.
可視性とは何ですか
Javaでは実は,publicやprotected,private
というキーワードをクラス,メソッド,フィールドに付けられます.
このキーワードの付け方により,どこからアクセスできるのかを制御できるようになります.
可視性のデフォルトはなしでprivateより弱く,protectedよりも強い制限です.
この可視性を使うときは,publicは最低限にする方が良いとされています.
リンクリストとは何ですか
順序を持つデータ集合を実現する方法の一つ. 各要素が次の要素へのリンクを持つことで順序を持つデータ構造を実現しています.
下のようにNode型が次の要素へのリンクであるNode型のフィールドと,
要素である valueフィールドを持ちます.
最初の要素さえ持っていれば,最後まで順番に辿れるようになります.

リンクリスト
Wikipediaの連結リストも参照すると良いでしょう.
インデントが面倒です
1行ずつ手作業でインデントしていくのは面倒な作業です. そのような作業はPCに任せましょう. ほとんどのエディタには,対象のプログラムを一括でインデントしてくれる機能が揃っています. そのような方法を調べて,利用してください.
- emacs
- Vim
- Visual Studio Code
- Atom
- Sublime Text
変数のスコープが短い方が良いのはなぜですか?
変数のスコープとは,変数の有効範囲のことであると学びました. プログラムを書く時には,一般的にこの有効範囲は狭い方が良いとされています.
スコープが広い場合,一度にいろんな変数のことを把握しておかなければプログラムの挙動がわかりません. 一方スコープが狭いと,ある場所(関数,メソッドやブロック)に関係する変数自体が少なくなります. すなわち,その場所に書かれた内容の理解がより容易であると言えます.
メソッド冒頭の変数宣言を止めよう
同様に,変数宣言も必要な箇所で行うようにしましょう. 関数,メソッドの冒頭に必要な変数をまとめて宣言するのは,宣言する箇所が決められていた時代の名残です. 今現在のC言語ですら,変数は関数の冒頭以外でも宣言できます. 必要な時に必要な変数を宣言するようにしましょう.
なぜmainメソッドに処理を書かない方が良いのでしょうか
Java言語は全てのプログラムが型(クラス)として作成されます.
staticキーワードが付けられていないメソッド,フィールドは,実体に対して1つ存在します(newすると異なる実体が作成され,実体ごとに同じ定義ながらも異なる値を持つ).
対するstatic キーワードが付けられたメソッド,フィールドは,型に対して1つ存在することになります.
Java言語では実体を作成してプログラムをするのに,型に対して1つしか存在できないstaticが付けられたメソッドに処理を書くのはJavaの書き方としてはあまり適当とは言えません.
プログラムを課題として捉えて,一度書けばあとは捨てると言う考えではある程度以上は上達しません.
後からどのように利用されるか,どのように拡張されるのか,と言う視点も持って取り組むようにしましょう.
そのためには,使い捨てになりがちな悪習を止める必要があります.
mainの処理を最小限にし,ifやforなどがないようなプログラムを書いていきましょう.
その指針として,クラス定義の基本形を挙げていますので,この指針に従ってプログラムを書いていきましょう.
キャメルケースって何ですか?
キャメルケース(CamelCase)とは,プログラム中で名前を書くための方法の1つです.
クラス名や変数名,メソッド名は,内容を表すために,複数の単語から構成されます.
その各単語の頭文字のみを大文字にし,そのまま繋げたものです.
ラクダのコブのような形になることから付けられています.
一番最初の単語の頭文字が大文字の場合をアッパーキャメルケース(UpperCamelCase),小文字のものをローワーキャメルケース(lowerCamelCase)と呼びます.
一方,各単語をアンダーバー(_)で繋げる方法をスネークケース(sanke_case),ハイフン(-)で繋げる方法をケバブケース(kebab-case)と呼びます.
スネークケースはC言語などで,ケバブケースはHTMLなどで用いられることが多いです.
変数名の付け方は各言語で推奨される方法がありますので,その方法に従うことをお勧めします.
Javaでは一般的に次のような規則で命名されます.
- クラス名: アッパーキャメルケース(
UpperCamelCase) - 定数:全て大文字のスネークケース(
SNAKE_CASE)- フィールドの型の前に
static finalをつけると定数として扱われます.
- フィールドの型の前に
- その他の名前: ローワーキャメルケース(
lowerCamelCase)- メソッド名,フィールド名,ローカル変数
各命名方法で書いてみると次のようになります.
- null pointer exception
- アッパーキャメルケース:
NullPointerException - ローワーキャメルケース:
nullPointerException - スネークケース:
null_pointer_exception - ケバブケース:
null-pointer-exception
- アッパーキャメルケース:
- Haruaki Tamada
- アッパーキャメルケース:
HaruakiTamada - ローワーキャメルケース:
haruakiTamada - スネークケース:
haruaki_tamada - ケバブケース:
haruaki-tamada
- アッパーキャメルケース:
System.exit は使わない方が良い?
Javaのプログラムを終了するための手段として,System.exit(0)が用意されています.
System.exitに渡す数値がステータスコードとなり,プログラムが終了します.
これは,他の如何なる処理が動作中であっても強制的に終了させます.
「なぜmainメソッドに処理を書かない方が良いのでしょうか」にも示した通り,Javaは型を作成します.
これは,作成したプログラムは他のプログラムからも呼び出される可能性がある,と言うことを指します.
そのようなJavaプログラムで闇雲に System.exit を呼び出して,プログラムを終了させていると,予想外の動作となることでしょう.
特に,WebアプリケーションをJavaで作っている時に,System.exitを呼び出すと,Webアプリケーション全体を終了させることになります.
そのようなことがないよう,System.exitの代わりに,returnで値を返すなどし,System.exitはmainメソッド内のみにするなどしましょう.
ヨーダ記法は避けるべき?
ヨーダ記法は,if分などの条件に通常とは異なる順序で記す記法です.
スターウォーズのキャラクタであるヨーダが通常の英語の語順とは異なる順序で話すことから名付けられたようです(Wikipedia).
例えば,value と 30 という数値との比較は,通常 value == 30 と書きますが,ヨーダ記法では,30 == value と書きます.
文字列同士の比較でも,stringと"Yoda" という文字列の比較を string.equals("Yoda")ではなく,"Yoda".equals(string) と書きます.
if(30 == value) { // value == 30 をヨーダ記法で記述
...
}
if("Yoda".equals(string)) { // string.equals("Yoda") をヨーダ記法で記述
...
}
このヨーダ記法により,== を間違って = と書くことに起因するエラーや string がnullであった場合に,NullPointerExceptionを避けられるなどのメリットがあるとされます.
しかし今日では, == と = の間違いは,コンパイラレベルで指摘されるべき内容でありますし,
通常とは異なる順序で書くことによる可読性の低下を許容できないという意見もあります.
さらに,NullPointerExceptionを避けられるというのも,nullをそもそも使わないプログラミングスタイルや,
Objects.equals を用いることで避けられるため,わざわざ読みにくいヨーダ記法を採用する理由は薄まっていると言えます.
なお,通常の順序とは,通常の文章の読み方に合わせて,変数を左辺,定数を右辺に書きます. 「value は 30 です」という文章と「30 は value です」という文章のどちらが自然に感じるでしょうか. この自然言語の語順と同じであることが読みやすさに繋がり,読みやすさに繋がらないプログラムはできるだけ避けた方が良いです.
参考
なぜメソッドに分けないといけないのでしょうか
プログラムコードをメソッドに分割することで,次のようなメリットが生まれます.
- 可読性が向上する.
- メンテナンス性が向上する.
- 再利用性が向上する.
加えて,メソッドの名前が適切に付けられているとメソッドの中を読まなくても,処理内容がわかるようになります. 例えば,以下のようなプログラムを考えてみましょう.
void run(String[] args) {
Integer maxIndex = findMaxIndex(args);
ArrayList<Integer> results = findPrimes(maxIndex);
printResults(results);
}
このように,各メソッドの中身を読まなくても findPrimes や maxIndex という名前から,
maxIndex までの素数の一覧を出力するプログラムなのだろうと予想できます.
一方,このようにメソッドに分けられておらず,
全ての処理を run メソッドに書いていると処理内容を全て理解しなければ,何のプログラムかが理解できません.
プログラムは書く時間よりも読む時間の方が圧倒的に長くなります. 書いた瞬間から読まれますし,時間が経ったあとに振り返って読む場合,誰かに見せる場合など,色々な機会で読まれます. そのため,書きやすさよりも読みやすさを重視した方が,費やす時間の合計は少なくなります. そのために,注意深く読んで理解するよりも,パッとみてどのようなプログラムかが想像した後に詳細を読み進めていく方が読むコストは低くなります. これらのことから,読みやすさを重視するためにも,メソッドに分けていきましょう.
どのようにメソッドに分ければ良いのでしょうか.
メソッドに分けるための目安として,行数が使われる場合が多いです. $n$行以上になればメソッドに分割しよう,ということですが,この基準$n$は人によってマチマチです. Thought Works アンソロジー という本では, メソッドは3行までと述べています. が,流石に$n=3$は制限が強過ぎます.
良いコ-ドへの道―普通のプログラマのためのステップアップガイド では $n=30$ 程度と述べています. また,メソッド 分割で検索してみると様々な$n$が提案されています. では,具体的な $n$ の値をどのように決めれば良いかというと,基本的には与えられた規則に従うのが良いでしょう. この授業では,$n$は 5〜20 程度が良いと考えています.
一方で,行数ではなく,処理の意味で分けるべきという意見も多くあります. 例えば,プログラムを書いていて,空行を設けている箇所はないでしょうか. 空行を入れるということは,そこで意味が変わっているケースが多いでしょう. その空行がメソッド分割の鍵になります. 空行の前後を別のメソッドに分割するのが良いでしょう.
インデントを揃えているのにインデントがズレていると指摘を受けました
Emacsを使ってインデントを揃えていると,タブとスペースが混在した状態でインデントされます. タブとスペースを混在させてのインデントがデフォルトになっているためです(ファイルサイズの削減のためという歴史的経緯でこのようになっているようです). そのような状態でターミナルや別のエディタでファイルをみるとインデントがズレた状態になってしまいます.
このようなことにならないよう,Emacsでのインデントもタブとスペースが混在しないように設定する必要があります.
Emacsの設定ファイルは,~/.emacs, ~/.emacs.el, ~/.emacs.d/init.el です.
この順で検索され,見つかればそれ以降のファイルは読み込まれません.
どの設定ファイルを推奨するかは,公式には一切書かれていないようです.
ただし,どのファイルも作成されていない場合は,~/.emacs.d/init.el にしておくと良いでしょう.
Emacsのインデントにタブとインデントが混在しないようにするには,Emacsの設定ファイルに次の1行を追加して,Emacsを再起動しましょう.
(setq-default indent-tabs-mode nil)
Javaのソースコードはどこで見られますか
Javaをインストールするとソースファイルもインストールされます.
Javaは /Library/Java/JavaVirtualMachines 以下にインストールされています.
どのようなものがインストールされているのかを確認してみましょう.
以下のコマンドを入力してください.なお,以下のzulu-17.32.13やzulu-8.62.0.19の部分は自分の環境に合わせて読み替えてください.
インストール方法や,Javaのバージョンによって微妙に異なる結果になっていますが,
おおよそ以下のような構造になっているはずです.
$ ls /Library/Java/JavaVirtualMachines/zulu-8.62.0.19/Contents/Home/
ASSEMBLY_EXCEPTION demo/ include/ lib/ man/ release src.zip Welcome.html
bin/ DISCLAIMER jre/ LICENSE readme.txt sample/ THIRD_PARTY_README
$ ls /Library/Java/JavaVirtualMachines/zulu-17.32.13/Contents/Home/
bin/ demo/ include/ legal/ man/ release
conf/ DISCLAIMER/ jmods/ lib/ readme.txt/ Welcome.html
Java 8 の場合は Home 以下に src.zip があり,
Java 11 以降は lib 以下に src.zip が置かれているはずです.
この src.zip が Java API のソースコードです.
展開するといくつかのディレクトリが作られますので,unzip -d src path/to/src.zip のように -d src をつけて展開すると良いでしょう.
講義資料に間違いを見つけました.
講義資料に間違いを見つけた場合は,間違いの報告をお願いします. バグ報告は https://github.com/ksuap/bugreport/issues からお願いします. 具体的な報告方法はこちらを参照してください. なお,バグ報告には,GitHub のアカウントが必要となります.