クラス名を小文字で始める
public class book { ... } のように小文字で書くとコンパイル自体は通ってしまうことがあるものの、Java の世界ではクラス名を小文字で始めるのは強い アンチパターン です。チームのコードレビューでも一発で指摘されます。必ず Book のように大文字始まりで書きましょう
レッスンの本文で注意している点を、つまずきの側から27件ぶん並べ直しました。書いた覚えのある形が見つかったら、 そのままレッスンに戻って前後を読めます。
public class book { ... } のように小文字で書くとコンパイル自体は通ってしまうことがあるものの、Java の世界ではクラス名を小文字で始めるのは強い アンチパターン です。チームのコードレビューでも一発で指摘されます。必ず Book のように大文字始まりで書きましょう
Book.java の中に public class Library { ... } と書くと、class Library is public, should be declared in a file named Library.java というエラーで止まります
public class を複数書くBook と Library の両方を public class で書こうとすると同じくコンパイルエラーになります
── 修飾子なしで String title; と書くと「パッケージプライベート」になります。最初のうちは public を明示的に書きましょう
static の付け間違い── インスタンスフィールドにすべき題名に static を付けてしまうと、全 Book オブジェクトでタイトルが共有されてしまいます
NullPointerException── String title; のまま book.title.length() を呼ぶと null に対してメソッドを呼ぶことになり、実行時にクラッシュします
static を付けたり外したりを間違えるクラス内のフィールドを使うインスタンスメソッドに static を付けると、non-static field cannot be referenced from a static context というエラーが出ます。逆にユーティリティ的な計算なのに static を外すと、毎回 new してから呼ばないといけなくなり不便です。「フィールドに触るかどうか」で判断しましょう
this を使わずに引数とフィールドを混同するメソッドの引数とフィールドが同じ名前のとき、title = title; と書いても何も起きません (引数が引数に代入されているだけ)。this.title = title; のように、フィールド側だけ this. を付けて区別する必要があります
return の値がずれるメソッドの宣言が public String getDescription() なのに return 123; と書くとコンパイルエラーです。戻り値の型と return で返す値の型は必ず一致させてください。何も返さないなら戻り値型を void に変える、というルールも忘れずに
new を書き忘れるBook b = Book(); のように new を抜いてしまうと、「Book という名前のメソッドを呼ぼうとした」と解釈されてコンパイルエラーになります。インスタンスを作るときは必ず new を先頭に付けてください
new を混同するBook b; だけで終わってしまうと、変数 b は宣言されていますが中身は null のままです。この状態で b.read() を呼ぶと実行時に NullPointerException が飛んできます。必ず = new Book(); まで書ききりましょう
static メソッドをインスタンスから呼ぼうとするSolution.helloWorld() のような static メソッドは、本来クラス名から呼ぶものです。インスタンスを作って s.helloWorld() と書いてもエラーにはなりませんが、警告が出るうえに意味も薄れます。static はクラス、それ以外はインスタンス、と使い分けましょう
─ public void Book(...) public String Book(...) のように void や String を入れてしまうと、Java のコンパイラはそれを 同じ名前のただのメソッド として扱います。引数付きで new Book("x", "y") のように呼ぼうとするとコンパイルエラーになります(コンストラクタとして認識されるものが引数なしのデフォルトコンストラクタしかないため)。一方、引数なしで new Book() と呼んだ場合はデフォルトコンストラクタが動くものの、フィールドは初期化されないままになります。public ClassName(...) { の形を厳守してください
─ public Book(String title) と public Book(String title, String author) の両方を用意した状態で new Book("Java入門") と呼ぶと、引数 1 個の方が選ばれます。new Book("Java入門", null) を呼ぶと 2 引数の方が選ばれ、author が null のまま fullTitle() を実行すると null が出力に混ざります。引数の数と意味は、コンストラクタを増やすたびに丁寧に確認しましょう
─ 自分で public Book(String title, String author) を 1 つ追加した瞬間、new Book() は使えなくなります。テストコードや別ファイルでまだ new Book() を呼んでいると、constructor Book in class Book cannot be applied to given types というエラーが出ます。引数なしも使いたいなら、public Book() {} を明示的に書き足してください
static メソッドの中で this を使ってしまうpublic static String describe() の中で this.title と書くと「non-static variable this cannot be referenced from a static context」と怒られます。static メソッドはクラス全体に属するので、「自分自身のインスタンス」が存在しません。インスタンスメソッド (static が付かないメソッド) でだけ this が使えます
this を省略したつもりが、ローカル変数を上書きしてしまうBook(String title) { title = title; } のように書くと、引数 title を引数自身に代入するだけでフィールドは空のまま。フィールドへ書き込みたいときは必ず this.title = title; と書くか、引数名を変える (_title newTitle など) ようにします
this(...) を 2 行目以降に書くthis(...) はコンストラクタの 先頭行 でなければなりません。何か別の処理を挟むと「call to this must be first statement in constructor」というエラーになります。共通処理は this(...) で呼ばれる側のコンストラクタにまとめるのが鉄則です
public にしてしまう書き始めのうちは、public int balance; のほうがコード量が少なくて楽に感じます。けれどそれは「これから先、誰かが変な値を入れても止められない」という時限爆弾を埋めている状態です。フィールドは原則 private、必要に応じて getter / setter を足す という順番を体に染み込ませてください。Java の世界では「private が当たり前、public フィールドは要相談」が共通認識です
IDE の自動生成でフィールドの数だけ getter / setter が並ぶと、見た目はリッチですが、実質的には public フィールドと同じ「中身を素通しできるクラス」になりがちです。setter は「外から自由に書き換えてほしい場合だけ」用意する、それ以外は読み取り専用にする (getter だけ作る)、という判断をするとカプセル化が一段強くなります。balance のように外から直接いじらせたくない値には、deposit(int) withdraw(int) のような 意図のあるメソッド を用意するのが理想です
private フィールドと getter / setter を律儀に書いていくと、Java のコードはどうしても縦に長くなります。意味のあるロジックが 1 行なのに、その周りに 50 行のボイラープレートがついている、ということもよくあります。これを Lombok の @Getter @Setter や Java 16 以降の record で短く書く、というテクニックもありますが、まずは「カプセル化は手間に見合った価値がある」と感じてもらえる程度の手書きに慣れることが大事です
フィールドだけ宣言して、コンストラクタの中で this.name = name; を書き忘れるパターン。コンパイルは通ってしまうのに、introduce() を呼ぶと name が null のままで "null (0)" のような結果が返ってきます。Java では String のフィールドは初期値 null、int のフィールドは初期値 0 になる仕様なので、入れ忘れに気づきにくいのが厄介です
コンストラクタを 1 つも書かないと、Java が自動で 引数なしのデフォルトコンストラクタ を作ってしまいます。すると new Person("太郎", 25) という書き方ができず、コンパイラから constructor Person in class Person cannot be applied to given types と怒られます。引数付きで初期化したいなら、その引数を取るコンストラクタを必ず自分で書く のが鉄則です
equals を実装していないのに == で比較する2 人の Person を比べたくて taro == hanako のように == で書くと、Java は メモリ上の同じ場所を指しているか だけを見ます。中身が同じでも new で別々に作っていれば結果は false です。中身を比べたいときは equals メソッドを自分で実装する必要があります
@Override アノテーションを忘れる付け忘れてもコンパイル自体は通ってしまうので、気づかずにメソッド名を tostring と書き間違えても、コンパイラは何も言ってくれません。すると toString() は親の実装が呼ばれ続け、いつまで経っても Point@1540e19d のままで「あれ、書いたはずなのに反映されない」とハマります。@Override を付けておけば、コンパイル時に即エラーで教えてくれます
Object.toString() は 引数なし のメソッドです。これを public String toString(int n) のように引数付きにしてしまうと、それは別のメソッドの オーバーロード (overload) になり、オーバーライド にはなりません。@Override を付けていればコンパイラが「親クラスに引数 int の toString はない」と教えてくれます
Object.toString() の戻り値は String です。これを void にしたり、Object にしたりすると、String 以外の戻り値を持つメソッドはオーバーライドとして認められません。やはり @Override を付けておけばコンパイラが守ってくれます