文末の ; を忘れる
Java は return "Hello, Java!"; のように、文の終わりに必ずセミコロンが要ります。これを忘れるとコンパイラに怒られます
レッスンの本文で注意している点を、つまずきの側から148件ぶん並べ直しました。書いた覚えのある形が見つかったら、 そのままレッスンに戻って前後を読めます。
; を忘れるJava は return "Hello, Java!"; のように、文の終わりに必ずセミコロンが要ります。これを忘れるとコンパイラに怒られます
文字列は "Hello" のように 必ずダブルクォート で囲みます。'Hello' と書くと「文字 1 個」の型 (char) になってしまい、コンパイルエラーです
return を忘れる戻り値の型を String と書いたのに return を書かないと「missing return statement」と怒られます。戻り値があるメソッドは必ず return で締めましょう
age = 20; のように、いきなり代入から書くとエラーになります。Java では最初に必ず int age = 20; のように型を書く必要があります
int 1stScore = 100; はコンパイルエラー。int firstScore = 100; のように英字始まりにします
int x; System.out.println(x); のように、値を入れる前にローカル変数を使うと variable x might not have been initialized と怒られます
int age = "20"; のように、int 型の箱に文字列を入れようとするとエラーになります。int age = 20; のように、型と値の種類を合わせましょう
int avg = (a + b) / 2; と書いて、平均が想定より 1 小さい、というのは定番の罠です。double avg = (a + b) / 2.0; のように、片方を小数にして回避しましょう
int x = 10 / 0; を実行すると ArithmeticException: / by zero が出てプログラムが落ちます。% も同様で 10 % 0 も例外になります。割る前に if (b != 0) でチェックするか、b == 0 の場合の戻り値を決めておくのが安全です
1 + 2 * 3 は 9 ではなく 7 です。「左から順番」と思い込んで計算するとズレます。迷ったらカッコを付けて自分の意図を明示しましょう
b = a++; と b = ++a; を「同じだろう」と思って書くと、b に入る値が違ってバグになります。代入や式の中で使うときは、必ずどちらか意識すること
int y = x++ + x++; のように 1 行で x を 2 回動かすコードは、読みづらいうえに動作の理解も難しくなります。Java では仕様上動作は決まっていますが、避けるべきコード の代表例です
n をそのまま n++ してしまう今回の課題のように引数の値を保ったまま +1 -1 した値が欲しい場合、n++ をすると n の値そのものが変わってしまいます。必ず別の変数にコピーしてから動かすこと
= と == の混同上で説明した通り。if (a = 5) と書いてしまうと Java ではコンパイルエラー、他の言語では「代入してから true 判定」という別の動きになる罠です
== で比較するString s1 = "hello"; String s2 = new String("hello"); s1 == s2 は意外にも false になります。文字列の中身を比べたいときは必ず s1.equals(s2) を使ってください。== はオブジェクトの「参照」が同じかを見るので、中身が同じでも別オブジェクトなら false です
== 比較double x = 0.1 + 0.2; x == 0.3 は false です。double は内部で 2 進数表現するため、計算誤差が生じます。浮動小数を比べたいときは Math.abs(x - 0.3) < 1e-9 のように「許容誤差以内か」で判定するのが定石です
& | (ビット演算) と && || の混同& 1 個と | 1 個は、int 用のビット演算子です。boolean にも一応使えますが、短絡評価が効かないので推奨されません。if の条件には必ず && || を 2 個重ねて書きましょう
! を忘れる / 付け忘れる「ログインしていない場合」は !isLoggedIn です。isLoggedIn のままだと逆の意味になります。日本語の「~でない」を ! に翻訳する習慣をつけてください
&& は || よりも優先順位が高いです。a || b && c は a || (b && c) の意味になります。意図したとおりに書きたいときは、迷わずカッコ () で囲みましょう
a + b * c を (a + b) * c と勘違いする数学の 2 + 3 × 4 と同じく、* が先です。Java の式を読むときは、まず * / % を探して心の中で先にカッコを補うクセを付けましょう
x = y = z の結合方向を逆にする代入 = は 右から左 に結合するので x = (y = z) と読みます。(x = y) = z ではないので注意。さらに代入式は = の右辺の値を返すため、if ((line = reader.readLine()) != null) のような書き方も存在します
& | と論理演算 && || の優先順位を混同するビット演算 & ^ | は等価演算子 == != や関係演算子 < > よりも優先順位が低く、論理演算子 && || よりは高い位置にあります。ただし & と && の本質的な違いは優先順位だけでなく 短絡評価 (short-circuit evaluation) の有無です。&& は左辺が false の時点で右辺を評価しませんが、& は必ず両辺を評価します。たとえば if (obj != null & obj.getValue() > 0) のように & を使うと、obj が null でも右辺が実行されて NullPointerException が発生します。論理判定では必ず && || を使ってください
"合計: " + a + b と書くと、a + b が最初に連結されてしまい、a = 1, b = 2 の場合 "合計: 12" になります。"合計: " + (a + b) のようにカッコで括れば "合計: 3" になります
1 + 2 + "円" は "3円" ですが、"金額: " + 1 + 2 は "金額: 12" です。String が登場した瞬間から、以降の + は文字列連結になります
"null" になるString name = null; String msg = "こんにちは、" + name; と書くと、msg は "こんにちは、null" という文字列になります。意図せず null を含んだ表示が出てしまい、ユーザーに気付かれて慌てる、というのは Java の現場でよくある失敗です。事前に if (name != null) でチェックするか、Objects.toString(name, "匿名") のようにデフォルト値を用意しましょう
(double)(a / b) のようにカッコ全体に掛けると、先に整数除算してから double 化することになり、小数部が消えます。(double) a / b のように 割り算する前のオペランド にキャストするのが正解です
long → int や int → byte のような縮小変換で、変換先の範囲を超えると値が壊れます。たとえば int n = (int) 3000000000L; は int の上限 (約 21 億) を超えるため、まったく違う負の数になります。(int) のキャストはコンパイラに「失われてもいいから変換する」と伝える宣言なので、警告は出ません。範囲を超えていないか自分でチェックする責任があります
(double) を片側だけに付けるのを忘れるint a = 7, b = 2; のとき、double r = a / b; と書いても結果は 3.0 で、3.5 にはなりません。整数除算してから double に拡大しているだけだからです。少なくともどちらか一方に (double) を明示しないと、小数演算にはなりません
{ } を省略してハマる─ if (x > 0) return "positive"; のように { } なしで 1 行書いた後、「処理を追加しよう」と 2 行目を足しても、その 2 行目は if の中ではなく外として扱われます。インデントが揃っているのでぱっと見では気づきにくく、デバッグに時間を取られる典型例です
if (x = 0) と書いてしまう─ 代入の = と比較の == の混同です。x = 0 は「x に 0 を代入する」という意味で、条件式としては成り立ちません。Java では boolean を期待する場所に int の代入結果が来るのでコンパイルエラーになり、最近の言語の中では比較的安全です
if (x); と書いてしまう─ if のカッコの直後にセミコロン ; を置くミスです。これは「条件が true のとき何もしない」という空文として扱われ、その後ろの { } は if と無関係なただのブロックになります。文法エラーにならないのでとくに厄介で、コードレビューで指摘されるまで気づかないことすらあります
else の前にセミコロン ; を入れるif (n % 2 == 0) { ... }; のように } の後ろに ; を書いてしまうと、その時点で文が終わってしまい、続く else が孤立してエラーになります。} else { の前に余計な ; を入れない、これが鉄則です
else に条件を書いてしまう (else if との混同)else (n > 0) { ... } のように、else の後ろに丸カッコで条件を書こうとする間違いです。条件付きの else を書きたいときは else if (n > 0) { ... } のように else if と書きます。else 単体は条件を取らない、と覚えてください
% と == の優先順位の勘違いn % 2 == 0 という式は % のほうが == より先に計算される ので、内部的には (n % 2) == 0 と評価されます。心配ならカッコを付けて (n % 2) == 0 と書いてもまったく問題ありません。むしろ可読性が上がるので、慣れないうちはカッコを付けるのもおすすめです
先ほどの grade の例。score >= 60 を一番上に書くと、95 点でも C 判定になります。厳しい条件を上、ゆるい条件を下 が鉄則
else if を elseif と書くJava では else と if の 間に半角スペースが必要 です。elseif と続けて書くと ';' expected や 'else' without 'if' のようなコンパイルエラーになります。Python の elif とも違うので注意
else を忘れて return が足りないif と else if だけ書いて else を書かないと、戻り値のあるメソッドでは missing return statement のコンパイルエラーになります。最後は else でガッチリ閉じる、または if の外側で return を 1 本用意する
>= を > と書いてしまう「90 以上」のつもりで if (score > 90) と書くと、ちょうど 90 点が "A" ではなく "B" に落ちてしまいます。「以上」は >=、「より大きい」は >、と日本語と記号を必ずペアで覚えてください
if (score >= 60) ... else if (score >= 70) ... のように下から並べると、70 点以上の人もみんな最初の score >= 60 でヒットしてしまい、"D" のままになります。else if チェーンは必ず狭い (厳しい) 条件から書く
if (score >= 90) と else if (score >= 80 && score < 95) のように、範囲が重なる書き方をすると、どちらにヒットするかが順番に依存してしまい、読む人を混乱させます。境界はきっちり閉じる
else を忘れて漏れを作るelse if (score >= 60) で止めて else を書かないと、50 点のときに "F" を返す経路がなくなり、コンパイラに「missing return statement」と怒られます。多段階の if の最後は必ず else で受ける
% と == の優先順位を勘違いするyear % 4 == 0 を「year % (4 == 0)」と解釈してしまう人がいますが、実際は % のほうが == より優先順位が高いので (year % 4) == 0 と評価されます。心配なときは自分で () を付けて意図を明確にしてもよいです
&& と || の優先順位を取り違える&& は || より優先順位が高いです。a || b && c は a || (b && c) の意味になります。うるう年式で (year % 4 == 0 && year % 100 != 0) のように カッコで囲んでいる のはこの理由で、可読性とミス防止のためにカッコを残すのがおすすめです
== と = の打ち間違いyear % 4 == 0 を year % 4 = 0 と書くと「year % 4 に 0 を代入」しようとしてコンパイルエラーになります。比較は イコール 2 個、代入は イコール 1 個、と暗唱して指に覚えさせてください
break を忘れる一番多いミス。case 1: の処理後に break; を書かないと、case 2: のブロックに勝手に流れ込みます。return で抜ける形なら起きませんが、System.out.println(...) などで終わる場合は 必ず break; を最後に書くこと
case の値の型ミスマッチswitch (day) で day が int なのに case "1": と書くと、incompatible types: String cannot be converted to int というエラーが出ます。case の値は switch の式と 同じ型 にする必要があります
default を書き忘れる戻り値があるメソッドだと missing return statement のコンパイルエラーで気づけますが、void メソッドだと黙って通り抜けてバグの温床になります。default は常に書く習慣を付けましょう
switch 式 (Java 14+) との混同Java 14 から追加された switch 式 は String s = switch (day) { case 1 -> "Monday"; ... }; のように -> (アロー) を使う新構文です。従来の switch 文 (case ... : + break;) とは別物で、構文を混ぜると ';' expected などのエラーが出ます。このレッスンでは古典的な switch 文の方を使います
? と : の順番を逆にする正しいのは 条件 ? 真の値 : 偽の値 の順番です。条件 : 真の値 ? 偽の値 のように書くとコンパイルエラーになります。「クエスチョン (?) が先、コロン (:) が後」と覚えましょう
a ? b ? c : d : e ? f : g のような書き方は、構造を理解していてもパッと意味が取れません。3 段以上のネストは避けて if-else や switch を使う
? の後と : の後で型が違う (例: n >= 0 ? "正の数" : -n) と、Java は「結果の型がひとつに決まらない」と怒ります。文字列と数値を混ぜないように、両辺の型を必ず揃えましょう
勝ちパターンを 2 つしか書かずに残り 1 つを忘れる、引き分けの判定を書き忘れる、といったケースです。テストケースを増やしてあげると、こういった抜けが必ず炙り出されます。先ほどの Mermaid 図のように、すべての分岐を絵にしてから書くと抜けが減ります
|| と && の使い間違いme == 0 || opponent == 1 のように、本来 && で繋ぐべき条件を || で書いてしまうミスです。|| は どちらか片方でも true ならパスしてしまうので、「自分がグー」というだけで勝ち扱いになるなど、判定が壊滅的に狂います。「両方とも成り立つ必要があるなら &&、どれか 1 つでも成り立てばよいなら ||」と毎回声に出して確かめてください
勝ちパターンだけ書いて、勝ちでなかったらすべて "lose" を返してしまうと、janken(1, 1) のような同じ手のときも "lose" になります。me == opponent をどこかで必ず判定する必要があります
─ if (n % 3 == 0) return "Fizz"; から書き始めてしまうと、15 のときも "Fizz" が返ってしまい、"FizzBuzz" の出番がなくなります。FizzBuzz (両方の倍数) を 必ず最初に 判定するのが鉄則です
String.valueOf を忘れて型エラー─ 戻り値の型は String なのに、return n; と書いてしまうと「int を String として返せません」というコンパイルエラーになります。整数を文字列にしたいときは String.valueOf(n) か Integer.toString(n) を使うか、もしくは return "" + n; のように空文字と連結する手もあります
% 3 && % 5 のような略記を使う─ n % 3 && n % 5 のように書きたくなる人がいますが、これは文法エラーです。&& の左右には boolean が必要なので、必ず n % 3 == 0 && n % 5 == 0 のように == 0 まで 書いてください
for (int i = 0, i < 10, i++) のようにカンマで区切るのは間違いで、正しくはセミコロン ; です
for (int i = 0; i < 10; i--) のように更新が逆向きだと i が永遠に増えないので、ループが終わりません。chotdekiru の Java Playground では 5 秒の実行制限で強制終了されますが、本番環境では CPU を食い続けてシステムが固まります
for (int i = 0; i < 10; i++) の i は丸カッコ内で宣言されているので、for ブロックの外では使えません
sum を初期化せずに使うint sum; とだけ書いて sum = sum + i; をやると、Java のコンパイラに「variable sum might not have been initialized」と怒られます。初期値 0 を必ず書きましょう
i < n にしてしまうi < n だと最後の n が足されません。sumOneToN(10) の答えが 55 ではなく 45 になります。<= と < の違いで結果がガラッと変わるので、ループ条件を書いたら頭の中で「i は最後にいくつまで到達するか?」を必ず確認するクセをつけましょう
int の桁あふれ (overflow)n がとても大きい (100000 以上など) と、合計が int の最大値 約 21 億 を超えて、マイナスの数になってしまうことがあります。今回の課題では n は小さいので気にしなくて OK ですが、本格的な数値計算では long 型を使うのが安全です
九九は n * 1 から n * 9 までです。for (int i = 0; i < 9; i++) と書くと i は 0〜8 になり、n * 0 = 0 が先頭に入ってしまいます。今回は i = 1; i <= 9; i++ が正解です
result = result + value + " "; のように書くと、最後の値の後ろにも空白が残ります。テストは "3 6 9 12 15 18 21 24 27" のように末尾に空白なしを期待しているので、if (i == 1) で分岐するか、if (i > 1) で先に空白を入れるパターンを使いましょう
i と n の取り違え文字列に並べるのは n * i の結果であって、i そのものではありません。うっかり result = result + i; と書くと "1 2 3 4 5 6 7 8 9" がいつも返ってきてしまい、n = 3 でも n = 7 でも同じ出力になります
while (n > 0) { count = count + 1; } のように n を更新する行を書き忘れると、n は永遠に正のままで、count だけが増え続けます。Java の executor では 5 秒で強制終了されますが、本物のアプリでは画面が固まって最悪のユーザー体験になります。ループの中で「条件に関係する変数を必ず変える」のは絶対のルールです
int count = 1; のように count を最初から 1 にしてしまうと、すべての結果が +1 ずれます。「ループに入る前に何回実行したことになっているか」をいつも意識して、初期値を決めましょう
0 や負の数) を考えていない今回は 0 を 1 桁として扱う仕様でしたが、これに気づかず while (n > 0) だけで提出すると、digitCount(0) が 0 を返してテスト失敗です。仕様の境界値は必ずテストケースに混ぜて、コードを書く前に紙に書き出すクセをつけましょう
> 0 と >= 1 の混同整数の n であればどちらも結果は同じですが、n > 0 と書くつもりで n >= 0 と書いてしまうと、0 までカウントが進んで "3 2 1 0" のように余計な 0 が混ざります。>= 1 か > 0 のどちらかで意図を統一しましょう
n-- の位置を間違えて無限ループresult = result + n + " "; の 前 に n-- を書いてしまうと、最初の値 n が結果に入らないまま 1 減って、想定とずれた文字列になります。さらに n-- 自体を書き忘れると n が永遠に減らず無限ループです
result + n + " " で連結すると、必ず最後にスペースが 1 つ余ります。"3 2 1 " のように末尾に空白が残った状態のままだと、テストの "3 2 1" と一致せず fail します。trim() で末尾の空白を必ず削ること
do { ... } while (条件) の末尾セミコロン忘れdo { ... } while (n <= 0) で終わると「; expected」というコンパイルエラーになります。do-while は文の最後に 必ず ; を付けるのがルールです。while 文と違うところなので意識して書きましょう
「正の数を見つける」課題なのに while (n > 0); と書いてしまうと、最初から正の数だったときに即終わり、負だったときは無限ループになります。「ループを 続ける ための条件」を書く、という基本に立ち戻ってください。n を上昇させて正を探すなら、ループ継続条件は n <= 0 (まだ正でない間続ける) です
do { ... } while (n <= 0); の中で n++ を書き忘れると、n がずっと初期値のままで条件が変わらず、永遠にループします。Java の Playground では実行が 5 秒で打ち切られますが、本番システムでこれをやると CPU 100% でサーバーが落ちます。ループ本体で必ず変数を更新する を呪文にしてください
i と j を混同する内側で書きたいのに i++ と書いてしまうと、外側のカウンタを勝手にいじることになり、無限ループや想定外の動作になります。「外側 i、内側 j」と最初に決めてから書きましょう
1 行ぶんの * を並べたあとに \n を追加しないと、全部が 1 行につながって *** のような出力になります。改行は内側ループの「外」、外側ループの「中」**が定位置です
pyramid(3) の期待値は "*\n\n*" で、末尾の \n はありません。毎回改行を付けてしまうと最後にも 1 つ余分に付き、テストが fail します。if (i < n - 1) で最後の行だけ改行をスキップするか、改行を行の「先頭」に置いて 1 行目だけスキップするテクニックを使いましょう
break と return を混同するbreak はループから抜けるだけで、メソッド自体は終わりません。一方 return はメソッドそのものを終了します。「ループの外でまだ処理を続けたいなら break、メソッドごと終わらせたいなら return」と使い分けてください。「break を書いたのに return まで処理が来てしまった」と慌てる人が多いですが、それは break の正しい動きです
continue で更新を忘れて無限ループwhile ループの中で continue を使うとき要注意です。int i = 0; while (i < 10) { if (i == 3) continue; i++; } のように、continue の前にループ変数を更新していないと、i = 3 のところで永遠に止まります。for ループなら更新式が自動的に走るので比較的安全ですが、while のときは continue の前に必ず更新を済ませる、または continue の中でも更新する書き方を意識しましょう
break の構文ミスラベルは outer: のように : をつけて for の 直前の行に単独で 書きます。for outer: や outer for のような書き方はコンパイルエラーです。break outer; の側はラベル名のあとに空白を入れて、最後に ; で締めます。たまにしか使わない構文なので、必要なときは公式ドキュメントで形を確認してください
─ 前章でも触れた話ですが、if (i % 3 == 0) を最初に書いてしまうと 15 のときも "Fizz" が返ってしまい、"FizzBuzz" が一度も出てきません。必ず i % 15 == 0 を最初に判定する か、i % 3 == 0 && i % 5 == 0 を最初に書きましょう
─ "1 2 Fizz 4 Buzz " のように末尾にスペースが残ると、テストの期待値 "1 2 Fizz 4 Buzz" と異なり fail します。trim() で取り除くか、最初から先頭判定方式で書きましょう
n が 0 のときに何が返るかを考えていない─ もし fizzbuzzList(0) が呼ばれたら、for は 1 回も回らず result は "" のまま trim() を経て空文字列が返ります。今回のテストでは n >= 1 しか使いませんが、業務コードなら エッジケース として「0 のときに何を返すべきか」を意識しておくとバグが減ります
void にしているのに値を return してしまうvoid は「戻り値なし」の意味なので、return "Hello"; のように値を返すとコンパイルエラーになります。値を返したいなら戻り値型を String や int に変更しましょう
return; だけ書いて値を返し忘れる逆に戻り値型を String にしているのに return; とだけ書くと「missing return value」エラーが出ます。戻り値の型に合った値を必ず指定してください
static の付け忘れchotdekiru の coding 問題は Solution.getGreeting() のように、クラス名から直接呼び出します。static を忘れるとインスタンス化が必要なメソッドになってしまい、テスト側から呼べずに fail します
public static int square(n) のように int を書き忘れると、Java は「n の型がわからない」と怒ります。引数は 必ず型から書く、と覚えましょう。同じ理由で int a, b ではなく int a, int b のように毎回型を書きます
メソッド側が square(int n) のように 1 個の引数を期待しているのに、呼び出し側で Solution.square() (引数なし) や Solution.square(3, 4) (引数 2 個) と書くと、method square in class Solution cannot be applied to given types のようなエラーが出ます。引数の 個数と型 が完全に一致していないとコンパイルは通りません
呼び出し側に int n = 5; という変数があると、「メソッド側の n と被るからダメなんじゃない?」と心配する人がいます。実際には別物として扱われるので、まったく問題ありません。むしろ呼び出し側でもメソッド側でも、その場で自然な名前を使うほうが読みやすいコードになります
return 文を書かない戻り値の型を String や int と書いたのに、メソッド本体のどこにも return がないパターン。エディタのエラー欄に「missing return statement」と出たら、まず全ての分岐に return が届いているか確認しましょう。if で return して else で return しないと、それだけで未到達の分岐ができてしまいます
return の後にコードを書く (dead code)return 0; の次の行に System.out.println("...") のような処理を書いてしまうやつ。デバッグ用の println を消し忘れる事故が多いです。return の後に書いてあるコードは絶対に動きません
return 値の型不一致int を返すと書いたのに "123" のような String を返してしまうケース。逆に String を返すメソッドで return 0; と書いてしまうのもよくあります。エラー「incompatible types」「cannot return a value of type X」が目印
rectangleArea(width, height) のつもりが rectangleArea(height, width) で呼んでしまうケース。長方形の面積では結果が同じなので気づきにくいですが、drawRect(x, y, width, height) のような関数では位置がズレて図形が変な場所に描かれます。呼び出すときは「メソッド宣言と同じ順番で書けているか」を必ず確認してください
int width, height のように書いて、int width, int height と書き忘れるパターン。Java のコンパイラから ';' expected <identifier> expected といった分かりにくいエラーが出ます。引数 1 個ごとに型を書く、を呪文として唱えてください
1 つ前のレッスンで書いた helloWorld() のような引数なしメソッドの感覚で、rectangleArea() と引数を渡さずに呼び出してしまうケース。コンパイラから method rectangleArea in class Solution cannot be applied to given types と長いエラーが出ます。引数を必要とするメソッドは、呼び出すときも同じ数だけ値を渡す必要があります
数学やスプレッドシートに慣れていると、最初の要素を arr[1] と書きたくなります。Java のインデックスは 0 始まりなので、最初は arr[0]、最後は arr[arr.length - 1] です。arr[arr.length] と書くと off-by-one エラー で ArrayIndexOutOfBoundsException になります
length をメソッド呼び出しで書くarr.length() と書きたくなりますが、配列の length は プロパティ なのでカッコは付けません。String の s.length() (カッコあり) との違いが地味に厄介で、コンパイラに cannot find symbol と怒られたら大抵これです
int[] arr = null; の状態で arr.length や arr[0] を触ると、即 NullPointerException で落ちます。引数で受け取る配列は、信頼できないなら if (arr == null) のチェックを先に入れる癖をつけましょう
arr.length と arr.length() の混同配列は length でフィールド、String は length() でメソッド。逆にすると「cannot find symbol」コンパイルエラーになる
arr[arr.length] で取ろうとする0 始まりなので最後は arr[arr.length - 1]。arr[arr.length] は範囲外で ArrayIndexOutOfBoundsException
一部の言語 (例えば Python) では arr[-1] で末尾要素が取れますが、Java では 負のインデックスはすべて例外。便利機能はないので、自分で arr.length - 1 を書く必要がある
length == 0 の配列で先頭にアクセスする空配列に対して arr[0] を呼ぶと例外。要素にアクセスする前に arr.length > 0 をチェックする習慣を付ける
i < arr.length を i <= arr.length にしてしまう配列の添字は 0 から arr.length - 1 までです。i <= arr.length だと最後の周で arr[arr.length] という存在しない要素にアクセスし、ArrayIndexOutOfBoundsException で落ちます。継続条件は必ず <、と決めてしまうのが安全です
, が残るresult = result + arr[i] + ", "; のように書くと、最後の要素の後ろにもカンマと空白が付いてしまい、"1, 2, 3, " のような出力になります。テストはこれを fail と判定します。先頭だけ特別扱い (if (i > 0) で前に ", ") で組み立てるのが定番のテクニックです
arr.length が 0 の場合に何を返すかを考えていないと、NullPointerException や思わぬ文字列を返してしまうことがあります。今回の課題では空配列は "" を返す決まりです。for (int i = 0; i < arr.length; i++) パターンなら自然にループをスキップしてくれるので、安心して使えます
sum を初期化しないint sum; と書いただけだと、sum += x; のところで Java のコンパイラに variable sum might not have been initialized と怒られます。必ず int sum = 0; と初期値を書きましょう
sum = x; と書いてしまうと、毎回 sum の中身が x で上書きされて、最後の要素しか残りません。arraySum({1, 2, 3}) が 3 を返してきたら、まずここを疑ってください。sum = sum + x; (または sum += x;) と書く、が累積パターンの命です
null チェックを書きすぎる / 書かなさすぎる今回の課題では arr が null で渡されることは想定していませんが、実務だと null が来る可能性があります。安全側に倒すなら if (arr == null) return 0; を冒頭に書きます。逆に「null ではなく空配列で渡す」と決めたなら、書く必要はありません。呼び出し側との契約をはっきりさせる ことが、null を巡るバグを防ぐ一番のコツです
return sum / arr.length; と書くと戻り値の型が double でも、右辺の int / int の結果 int が double に変換されるだけです。arrayAvg(new int[]{4, 7}) で 5.5 を期待しているのに 5.0 が返ってきて「あれ?」となります。割り算の前に、片方を (double) でキャスト しましょう
(double)(sum / arr.length) は意味がありません。括弧の中で先に int の割り算が走ってしまうからです。(double) sum / arr.length のように、キャストは割り算の左側の値だけに掛けるのが正解です
if (arr.length == 0) return 0.0; を忘れると、arr.length が 0 のとき sum / 0 を計算しようとして ArithmeticException や NaN が出てきます。割る前に必ず分母が 0 でないかをチェックするのが鉄則です
max = 0 で初期化して負の数で失敗int max = 0; で始めると、配列が [-1, -5, -3] のように 全要素がマイナス のとき、答えが 0 になってしまいます。実際の最大値は -1 なのに、です。max の初期値は arr[0] または Integer.MIN_VALUE にするのが鉄則です
IndexOutOfBoundsExceptionint max = arr[0]; を書いた直後に、空配列 [] が渡されると、arr[0] にアクセスした瞬間に例外が飛びます。必ず if (arr.length == 0) return 0; のような 空配列ガード を先頭に置いてください
>= と > の違い更新条件を arr[i] >= max と書いても答え自体は同じになりますが、>= だと「同じ値」に出会うたびに上書きが走るので、わずかに余計な代入が発生します。> で「より大きいときだけ更新する」のが教科書的な書き方です
for の中で return i; ではなく、int found = i; のように変数に保存するだけにしてしまうと、後ろにある同じ値で found が上書きされてしまうことがあります。「先頭から見て 最初 に見つかった位置」という仕様なので、見つけた瞬間に return で抜けるのが正解です。どうしてもループの後で返したい場合は、見つかった時点で break; してから return found; する形にしましょう
index の初期化忘れ / 初期化ミス拡張 for で書くときに int index = 0; の宣言を忘れると cannot find symbol のコンパイルエラーになります。逆に int index = -1; から始めてしまうと、arr[0] が target と一致したときに -1 が返ってしまい、テストが fail します。初期値は 0 が正解です
arr = [10, 20, 10, 20] で target = 10 の答えは、0 (最初の 10) であって 2 (後ろの 10) ではありません。普通の for ループで先頭から return する書き方になっていれば自然に 0 が返りますが、「最後に一致した index を返したい」と仕様を勘違いすると 2 を返すコードを書いてしまいます。仕様を読むときに「最初」「最後」「全部」のどれを期待されているか必ず確認するクセを付けましょう
for-each で要素を書き換えたつもりになるfor (int x : arr) { x = ... } は元の配列に反映されない。書き換えたいときは通常の for 文を使い、arr[i] に直接代入する
: の代わりにカンマやセミコロンを使う通常の for 文の影響で for (int x ; arr) や for (int x , arr) と書いてしまう。正しいのは コロン : 一文字 で、それ以外は全部コンパイルエラー
int[] の配列に対して for (String x : arr) とやると incompatible types のエラーになる。要素の型と一致させる必要があり、自信がなければ var (Java 10 以降) でコンパイラに推論させるのも手
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 を付けておけばコンパイラが守ってくれます