Javaのコーディングルール
package jp.avaj.lib.algo; /** * Javaのコーディングルール * ・包括的に述べたものではない.他は一般常識、自分のルール、プロジェクトのルールに従うこと. * ・偏屈(笑)なものもあるので、吟味・納得したうえで使用すること. * ・ルール集なので「ルールAを実施すればルールBは不要あるいは不可能」なものは含まれている. * ・本クラスを実行すると、(解説を除いた)ルールだけが表示される. * ・私が実践していないルールもある.どれが該当するかは秘密. */ public class A_JavaCodingRules { // Artery-アルゴリズム-機能別サンプルの目次 A_Contents00 a_Contents00; // https://artery3000.hatenablog.com/entry/2019/06/24/112558 public static void main(String[] args) { p("□□□□ Javaコーディング規約 □□□□"); p("他は一般常識、自分のルール、プロジェクトのルールに従うこと"); p("\n□ 変数・定数"); p("boolean(Boolean)は使用しない⇒enumを使用する"); { // booleanはメソッドの引数にした時に分かりにくい. // メソッドの仕様を確認しないと、分かりにくい. // enumにすれば意味が分かりやすい. // メソッドの中でのローカルな使用やメソッドの返値にするのは、なんらかまわない. } p("boolean(Boolean)は再定義して使用する"); { boolean isActive = true; // 上記よりも、以下の方が分かりやすい. boolean ACTIVE = true; boolean IN_ACTIVE = false; boolean status = IN_ACTIVE; // メソッドの引数にした場合も意味が分かる⇒しかし違う意味のbooleanを渡してもコンパイルエラーにはならない⇒本当はenumがよい. } p("boolean(Boolean)は否定形の名称は使わない"); { // 理由はソースを読んでいて分かりにくい. // 否定条件にした時に特に分かりにくい. // 他の条件と複合した時に分かりにくい. boolean notFound = true; if (! notFound) { // ←分かりにくい // 見つかった時の処理. } } p("いろいろなところで使用される変数には(できるだけ)Stringを使用しない⇒String内包のクラスにする"); { // 理由はStringでは分かりにくいから. // 名前もString,住所もString,会社名もString,部署名もStringでは分からない. // 変な代入をしても、引数を間違えてもコンパイルエラーにならない. // 本当はclass CompanyName extends String{}としたいができない⇒Stringはfinalなので. // なのでclass CompanyName { private String value; ...}とする. // しかしすべての局面で、これをやろうとすると、かなり面倒. // Stringのメソッドを(必要なものだけでよいが)再定義する必要がある. // 外部のライブラリを使うときに、常に変換・逆変換をする必要がある. } p("nullは再定義して使用する⇒クラス名+IS_NULL"); { // nullは代入する時や、メソッドに渡す時に、意味が分からない.特にメソッドに渡す時. class Address { // 処理省略 } class MyClass { public void method(Address addr) { // 処理省略 } }; // これでは、何のnullを渡したのか分かりにくい MyClass myObj = new MyClass(); myObj.method(null); // ⇒ 分からない // 以下のようにするとよい Address Address_IS_NULL = (Address)null; myObj.method(Address_IS_NULL); // ⇒ 分かりやすい // 注、null定義は、本来はクラス(この例ではAddress)の中に定義した方が良いと思われる. // Address.IS_NULLと引用する // しかし自分の担当ではない時は、上記のようにする. } p("int定数は使用しない⇒enumを使用する."); { // intであれば、不正な値が代入されてもチェックされない. // しかし既存システムでint定数を使用していれば、int定数の使用もやむを得ない. // intでの性別定義 final int GENDER_MALE = 1; final int GENDER_FEMALE = 2; int intGender = GENDER_MALE; intGender = 100; // ⇒ コンパイラエラーにならない // 上記よりもenumにする Gender enumGender = Gender.MALE; } p("int定数はInteger変数は使用しない⇒==判定のバグを避けるため."); { // new Integer(0) == new Integer(0)はfalseとなる // これでバグったら分からない. } p("離散状態を表すString定数は使用しない⇒enumを使用する"); { // ・これもint定数を使用しないのと同じ.不正な値が代入されてもチェックされない // ・しかし既存システムでString定数を使用していれば、やむを得ない. final String GENDER_MALE = "male"; final String GENDER_FEMALE = "female"; String stringGender = GENDER_FEMALE; stringGender = "abc"; // ⇒ コンパイルエラーにならない // 上記よりもenumにする Gender enumGender = Gender.FEMALE; // 補足. // DB上では数値または文字でメモリ上でenumであれば相性が悪い.相互変換する必要がある. // 当該enumにstaticのfromInt(fromString)を実装し、toInt(toString)を実装すれば、変換を行う汎用クラスが作成できる. } p("事前に定義され実行時に変化しないものはListではなく配列にする、ロジックで生成するものは配列ではなくListにする"); { // これは確たる論理的な根拠はないが、上記のイメージが(私には)ある. // 配列にするかListにするかは、悩む時があるが、このような原則を決めておくとよいだろう. } p("定数をハードコーディングしない."); { // 単に定数を書いたら、意味が分からない. // 修正する時に、同じ値の別の部分を修正してしまう可能性がある. // これでは意味が分からない、statusの1を他の値に変更する時にgenderも間違えて変更するかも. int status = 1; // 意味が分からない int gender = 1; // 意味が分からない // このようにすると、分かりやすくなり、変更するにも間違いがなくなる. final int STATUS_ACTIVE = 1; final int STATUS_INSCTIVE = 2; final int GENDER_MALE = 1; final int GEDNER_FEMALE = 2; status = STATUS_ACTIVE; // 分かりやすい gender = GENDER_MALE; // 分かりやすい // 注、この例は、本当はenumに変えた方がよい } p("変数の使いまわしは避ける"); { // ひとつの変数は、一つの意味だけに使用すべき. // 分かってはいるが、忙しい時は間違うこともありえないわけではないので注意する. } p("変数への再代入をなるべく避ける"); { // 変数に複数回代入していると、その変数を使って処理する時に、値が分かりにくくなる. // 何をもって「再代入」とするかは微妙. // 例えば以下のものは、問題ないだろう. // ループでインスタンスを次々と処理する時に、インスタンス変数や関連変数に値を代入する. // 初期値を設定し何らかの条件で、その値を更新する、その後、その変数を使って処理する. // ダメな場合の例. // 変数に値を代入後に、その変数を使って処理し、さらに値に代入して、またその変数を使って処理する. // 「まとまった処理はブロックにする」を参照. } p("\n□ クラス定義"); p("インスタンスクラス(仮称:データを入れるクラス)はフィールドをまとめて先頭に記述しフィールドコメントは一行にする."); { // 理由はフィールドの一覧をすばやく把握するため. // インスタンスクラスでは、フィールドの他にはsetter/getterなど決まりきったメソッドが定義されている. // ソースを見る時には、どのようなフィールドがあるかを先頭部分を見て一瞬にして把握できるようにしたい. // なのでフィールド定義だけを先頭部分に記述し、フィールドのコメントも改行しないで一行に記述する. } p("ロジッククラス(仮称:ロジック中心のクラス)はフィールドは最後に記述する"); { // ロジッククラスでは、publicなメソッドを把握したい. // 内部処理に使うフィールドは、クラスを利用する立場からは不要なので、最後に記述する. // publicな変数・定数は、先頭に記述すべきだが、privateな定数は最後に記述したほうが良い. } p("classのpublicは必要な時だけつける"); { // 明らかに自パッケージ外から参照されるものは、publicにする. // 以外のクラスは(とりあえず)publicはつけない. // もし必要になれば、その時点でpublicにするが、その時に「これはpublicにすべきか否か?」と考えるタイミングが生まれる. // 弊害としては、他のパッケージの担当者が、使えるクラスの存在を知らずに、同じようなクラスを作ってしまうことがありうる. } p("コンストラクタでは、実質的な処理は行わずフィールドの初期化くらいにする"); { // 必須ではないが、常識だろう. // 普通コンストラクタでは、あまり処理は入れないので、他の何かの処理を入れると、他の人が誤解する可能性がある. } p("フィールドの初期化はメソッドにしてfinalをつける."); { // フィールドは宣言行で初期化しない.初期化メソッドの中でやる. // 何を初期化しているか分かりやすくなる. // 再初期化が必要になった時に便利. // finalにするのはオーバーライドを防ぐため.コンストラクタで呼び出すメソッドをオーバーライドするとおかしくなる.詳細省略. // 継承したクラスの初期化メソッドの名称は別途考える必要がある. } p("フィールドの初期化メソッド名はシステム内で統一しておく(initなど)"); { // 誤解を防ぐためには当然のこと. // 他の目的のメソッド名を、初期化メソッドと同じにしないこと. } p("コンストラクタやメソッドで処理がないときは「処理なし」と記述する"); { // コンストラクタ // 処理がないときは、その旨を明示するのが正しい. // ただし、フィールドの初期化をメソッド化していれば処理はあるはず.ないのはstaticメソッドばかりのクラス. // メソッド // 処理がないメソッドがありうるか?⇒メソッドをオーバーライドした場合にはありうる. // 「処理なし」と記述しておく⇒この場合に記述することは重要. // 「処理が必要か否かを考えた上で、必要がないと判断した」ことが分かる. } p("インスタンスクラスはComparatorを作成する"); { // 必要がない場合も多いが、一応「作成する」という原則にしておく. // 個別判断で「このクラスでは不要」とすればよい. } p("複数のロジッククラスから利用されるインスタンスクラスはインターフェースを分けることも考える."); { // 他のロジッククラスに、インターフェースで渡すことも考える // 受け渡したクラスに勝手なことをさせない. // 受け渡したクラスが何をすべきかはっきり分かる. // そのためだけにインターフェースを作成するとよい(かも). } p("定数だけのインターフェースを作成しない⇒クラスにする⇒implementsされるとわからない"); { // 理由は上記の通り.クラスであればクラス名を記述するので、何の定数か、どこで定義されているかわかりやすくなる. // ただしできるだけenumにする. } p("staticブロックで例外を発生せざるを得ない場合⇒ログ&RuntimeException"); { // 注意、これはかなり例外的な場合. // 共通クラスにstaticな定数があって、それ初期化するときに、staticブロックを使うと便利. // 最初に使われたタイミングで実行されるので、実行タイミングを考慮する必要がない // しかしstaticブロックでは例外を出せない(←コンパイルエラーになる). // どうしてもstaticブロックの処理で例外がでる場合は、内部でcatchしたうえで、ログを出してRuntimeExceptionをthrowすれば、コンパイルは通る } p("必要に応じてクラス内にローカルなクラスを作成する"); { // 他で使用しないクラスは、ローカルで定義する. // 外に出すと目障り、誤解のもと. // 変数をできるだけ、狭いスコープに制限するとの同じ. } p("\n□ 実装・継承"); p("標準ライブラリのComparator,Iteratorなどはダミーを作成し、それを使用する"); { // 理由はComparatorの一覧がJavaDocですぐに分かるから. // 以下のようにする. // interface OurComparator extends Comparator {} // class OurClass implements OurComparator { .... } } p("実装、継承では元の名前を先頭につけた名前にする"); { // 理由は次の通り // 名前で何を実装・継承しているか、すぐに分かる // Eclipseで隣に表示される. // 例えば次のようにする. /** 社員. */ class Staff {} /** 管理職. */ class StaffManager extends Staff{} // 問題点 // 英語的にはしっくりこない名称になる. // 複数の実装・継承がある場合には、一つに限定する必要がある. // 階層が複数になる場合は、名前がだんだん長くなる. } p("基底クラスのメソッドを使わせたくない時はRuntimeExceptionにする"); { // 派生クラスを作ったとき、基底クラスのメソッドが不要になることがある. // そのメソッドをオーバーライドしてRuntimeExceptionを発生させれば、間違った呼び出しを防ぐことができる. } p("クラス定義やメソッドには必要な時にはfinalつける"); { // これは常識だが、なかなか実行されていないようである.私もそう. // なのでルール化しておいて、忘れないようにする. } p("インスタンスクラスはtoStringを実装する"); { // デバッグなどに有用なので、必ず実装したい. // 必要なフィールドをtoStringしてつなぎ合わせればよいので、汎用的なヘルパーを作ることができる } p("インスタンスクラスはequals,hashCode,Comparable,cloneを実装する."); { // これらは不要なことも多い. // 実装するというルールにしておいて「このクラスでは実装しなくてよい」と個別判断する. } p("\n□ メソッド"); p("メソッドの名称に否定形は使用しない"); { // 特に返値がbooleanの場合. // boolean変数の名称と同じで分かりにくくなる. // 分かりにくくない場合もあるので、一応個別判断が必要 } p("Listなどの返値は値がない時はemtpy(サイズがゼロ)よりもnullを戻す"); { // これはいつも判断に迷うところ. // emptyの場合は、データが取れた時となかった時の処理が同一にできる.これがメリット. // しかし明示的に判断した方がよいとも言える. // どちらにするかはシステムで統一したらよいということにしておく. // 「返値が正常かどうかを判定するstaticメソッドを元のクラスに作る」を参照. } p("返値が正常かどうかを判定するstaticメソッドを元のクラスに作る"); { // 返値を戻したら後はどうするかは呼び出し側の勝手になる. // メソッドの意図通りに解釈してくれるかは不明である. // 少しでも改善するために以下のようにする class MyClass { public String method(String str) { // 何らかの処理 return null; } // 注、本当はstaticメソッドにする.ここでstaticにするとコンパイルエラーになる. public /* static */ boolean errorHappened(String value) { return (value == null); } } MyClass myObj = new MyClass(); String returnValue = myObj.method("aaaa"); // 本当は以下のようにする //if (MyClass.errorHappened(returnValue)) { // // エラー処理 //} // 仮に以下のようにしておく if (myObj.errorHappened(returnValue)) { // エラー処理 } // 「やりすぎ」という批判があるのは承知している. } p("返値がbooleanのメソッドは、if文に使用して馴染む名前にする"); { class MyClass { public boolean check(String str) { // 何かの処理 return true; } public boolean checkNormalEnded(String str) { // 何かの処理 return true; } } MyClass myObj = new MyClass(); // これではif文の意味がよくわからない if (myObj.check("aaaa")) { // 何らかの処理 } // こちらだと意味が分かる if (myObj.checkNormalEnded("aaaa")) { // 何らかの処理 } } p("オブジェクト内部のList,Set,Mapなどはメソッドの戻り値にしない、必要ならばコピーを戻す"); { // 内部のListなどを戻したら勝手にいじくられる危険性がある. // なのでコピーを戻す. // 蛇足、ArrayListはCloneableだがListはCloneableではない } p("複数の返値を戻したいときにどうするか⇒クラスを作る"); { // 返値用のクラスを作ることになる. // しかしそのクラスが、他で使えないようであれば、非常に悔しい. // でも普通は、悔しさを我慢して、このようにする. } p("複数の返値を戻したいときにどうするか⇒オブジェクト配列を戻す⇒値の取出しstaticメソッドを作成する"); { // 前項で「どうしても嫌だ」という場合には、次のようにする class Box { public Object[] getSize() { // 最初が高さ、次が幅のつもり return new Integer[]{5,10}; } // ここも本来はstaticメソッドにすべきだがコンパイルエラーになる public /*static*/ Integer getHeight(Object[] values) { return (Integer)values[0]; } public /*static*/ Integer getWidth(Object[] values) { return (Integer)values[1]; } } Box box = new Box(); Object[] values = box.getSize(); // 本来はstaticメソッド int width = box.getWidth(values); int height = box.getHeight(values); } p("内部データが不必要なメソッドはstaticにする"); { // newする必要がない、クラス名を書くので意味が分かりやすい // このようにするのが常識だと思うが、時々インタンスメソッドにしているものを見かける } p("\n□ ロジック"); p("処理スピードよりも分かりやすいプログラミングを心がける"); { // 現在では当然の認識.もしスピードが必要ならば、そのようにする. } p("まとまった処理はブロックにする"); { // 一つの処理を実現するために、複数行を記述することがある.例えば、いくつかの変数に値を入れて、メソッドを呼び出すなど. // このような時に、これらの一連の行をブロックにして、一段インデントを下げる. // ブロックにすることで、これらの行が一連のまとまった処理であることが分かる. // ブロック全体にコメントを付けることができる // ブロック内部に変数を定義できるので、変数の誤爆を防ぐことができる. // これはブロック全体のコメント、ブロックの中に一連の処理を記述する { int a = 0; String str = "abc"; // 以下続く. } } p("else文では改行する"); { // 多くの人は以下のようにif~elseを記述する int a = 1; if (a == 1) { // 必要な処理 } else { // 別の処理 } // それを次のように記述する // 理由はelse全体にコメントを付けることができるから // 上の「まとまった処理はブロックにする」を参照. if (a == 1) { // 必要な処理 } // elseの場合の処理⇒ここがelse全体のコメント else { // 別の処理 } } p("三項演算子を使う"); { // これは当たり前だが、使っていないプログラムは、よく見かける. // 行数が多くなり、冗長、見にくい、いらいらする. // ただし入れ子の三項演算子は使用しない. } p("Stringの等値判定はequalsを使用する"); { // ==で判定しているプログラムをたまに見かける.じっと見ていると貧血になりそう. // しかもテストを通っていたりする.通る理由は、コピーで回していれば、等しくなるため. } p("Stringの等値判定は定数を主語にする"); { // これはnullで落ちるのを防ぐため. // ただし、きちんとnullチェックが必要な時は、前の行でチェックする必要がある String str = "bbbb"; if ("aaaa".equals((str))) { // 注、本当は"aaaa"は定数定義すべき // 一致した時の処理 } } p("Booleanはnewしない"); { // Booleanはtrue,false(とnull)しかなく、それはBooleanクラスに定義されているのでnewする必要はない // 注、(new Boolean(true)) == (new Boolean(true))はfalseとなる. Boolean boolValue = Boolean.TRUE; } p("nullは型を明示する"); { // 変数に代入した時はともかく、引数に渡したときは間違いが発生しやすい. // 不安になって相手側のメソッドを見て確認することになる. class MyClass { public void method(String str) { // 何かの処理 } } // 以下のようにする MyClass myObj = new MyClass(); myObj.method((String)null); // 本当は、nullを再定義したほうがよい. // 「nullは再定義して使用する⇒クラス名+IS_NULL」を参照. } p("if,for,whileは一文でもブロックにする"); { // 理由は、文を追加しようとしたときにバグるから. } p("順番を意識するループでは拡張for文を使用しない"); { // 使用しても結果は同じだが、ソースコード上「順番に処理している」ということが見えないから. } p("分岐(if,switch)を使わずに定数テーブルで切り分けることも考える"); { int a = 1; String msg; if (a == 0) { msg = "aaaa"; } else if (a == 1) { msg = "bbbb"; } // 以下続く. // 以下のようにする String[] msgs = new String[] {"aaaa","bbbb"}; msg = msgs[a]; // この例はありがたみがないが、状態マシンなんかを作るときには、このようにしないと混乱する. // 注、元の値がゼロオリジンでないときは、配列を二段にしたりする必要がある // 定数テーブルだけなので、短くなり、見やすい⇒バグが少なくなる } p("elseで処理がない時は「処理なし」のコメントをしておく"); { int a = 0; if (a == 0) { // なんらかの処理を記述 } else { // 処理なし } // 当然ながらすべてのif文に、このようにはしない.疑問が入り込む余地がある場合のみ. // 「elseの処理が必要か否かを考えたうえで、不要と判断した」ということを伝えることができる. } p("if文の複合条件を分解して、ネストにする"); { // if文で条件がいくつもandで結びついて分かりにくい場合がある. // このような時は、条件を分解してif文をネストにする. // 「素人っぽい」という批判を受けても、バグを入れない、分かりやすくすることを重視すべき. } p("複合条件は括弧で分離・明示する"); { // いくつもの条件がand/orで結びついている場合は、それぞれを()で括って分離すると見やすくなる. } p("オブジェクトをできるだけ使いまわししない"); { // オブジェクトの生成コストがもったいないので、使いまわしをするとバグることがある // 気にしないで、新しいオブジェクトを作るほうが良い. // もちろんパフォーマンス上必要な場合は使いまわす. } p("staticメソッド呼び出しは必ずクラス名にする.変数名にはしない.自クラスでもクラス名を書く、"); { // staticメソッドを呼び出していることが明確になり、ソースが見やすくなる. } p("StringBuilder(StringBuffer)はできるだけ使わない"); { // これは批判を受けるだろうが、ソースの見やすさの方が大事. // 確かにパフォーマンスは違う.しかし多くの場合はそもそも絶対時間が少ない. // 非常に多くのループをする場合などを除いてStringBuilderは使わないようにする. // 注、私の計測ではStringの追加処理でStringBuilder(StringBuffer)使用しない時は約10倍かかる. } p("単体テストしやすいようにメモリ上のロジックをメソッドに外だしする"); { // これも状況判断になるが、ある程度は外出しにしたほうが良い. // しかし、やりすぎるとメソッド数が多くなって混乱する } p("\n□ 共通クラス"); p("日時処理は共通クラスを作成する⇒フォーマットの統一、日時処理の統一"); { // 日時の処理は、システムのいろいろなところで、同じような処理をしているのを見かける // 同じフォーマットが、いろいろなところに記述されている. // また特に日付処理を行う場合、時刻の誤差でバグが入りこむことがあるので、防ぐ必要がある. } p("リフレクション、通信、IOなどは共通クラスを作成する⇒他のロジックと混在を避ける"); { // DBアクセスをDAOとして外出しにするのと同じで、これらのような、ちょっと特殊な処理は共通クラスにする. } p("プロジェクト内の共通クラスの整備は、コストアップになるが、推進すべき"); { // プロジェクト内で共通クラスを整備するのは、むしろコストアップ要因⇒しかし整備すべき. // 整備するコスト、メンバーが仕様を把握するコストの方が上回る. // プロジェクト内での共通クラス整備は、品質の向上のためである.これを認識してないと挫けてしまう. } p("企業内であれば、長期的展望をもって、整備していくべき"); { // 長い目で見れば、コストダウンにもつながってくる. } } // private static void p(String s) { System.out.println(s); } /** 性別のenum. */ static enum Gender { MALE, FEMALE; } } //・Arteryライブラリは以下よりダウンロード可能 //・Vector⇒ダウンロード-Windows⇒プログラミング⇒Java言語