■ final 修飾子
はじめに
Javaには継承やオーバーライドなど便利な機能があることを学んできました。
しかし場合によっては元のクラスやメソッドの機能を変えるべきでないときもあります。その時に使うのが final修飾子です。
Step1: 概念を知る
final修飾子
決めたことを変更できなくするように、プログラマーが指定する機能です。
クラス、メソッド、変数に指定することができ、その働きは以下の通りです。
①クラスに付けた場合 → そのクラスを継承することができなくなる
②メソッドに付けた場合 → そのメソッドをオーバーライドすることができなくなる
③変数に付けた場合 → その変数は変更不可 (定数)となる
Step2: 使い方を知る
final の使い所
変数として扱う必要が無い場合は final を付与する癖をつける。
特にメソッドのパラメーターや、メソッドのローカル変数において、条件分岐ごとの処理による値の書き換えの可能性が無いような場合は、final で定義するほうがよいです。
【サンプル: 定数】
・定数を定義する
固定で表示するダイアログのメッセージや、基本的に一度確定したらそれ以降変更する必要がないような値に関しては定数として定義しておくとよいです。(※業務上は、定数を定義することを 「定数を切る」などと表現したりします。
また、個々のjavaファイルへ定義するよりかはCommonConstant などと定数専用のクラスを定義して、その中にまとめて定義することが多いです。
【サンプル: ロジック】
■仕様(ざっくり)
入力値を受け取り、入力値に沿ったメッセージを返し、コンソールへ出力するとしましょう!
1.validateInput メソッドのパラメーター String paramInput
2.validateInput メソッドのローカル変数 String message = null;
3.validateInput メソッドの返り値を扱う String message
もし仮に上記のサンプルをAさんがコーディングしたとしましょう。
その後、仕様を知らない (or 忘れている) Bさんが修正を入れたとした場合、message の値が message = "テキトーな値" と書き換えられてしまう可能性は否めません。
そのため、上記3つのうち1、 3に関しては仕様を考慮した上で見ていくと使用箇所以外での使いみちが無いため、 本来であれば final が望ましいです。
こういう些細な気配りが見えるコードは、バグを生み出す原因を潰してくれているので共同開発者としてはとても嬉しいものになります。
■static 修飾子
はじめに
まだstaticの意味がよく分かっていない人も、書いたことは何回もあるんじゃないでしょうか?
mainメソッドを書く時に出てきますよね。 mainメソッドはstaticなメソッドなんです。
Step1: 概念を知る
・static 修飾子
メソッドや変数に付けることによって、インスタンス化しなくてもクラスから直接メソッドや変数にアクセスすることができるようになります。
staticなメソッドや変数のことを静的メソッド、静的変数あるいはクラスメソッド、クラスフィールドと呼びます。
一方で、static を付けずにインスタンスを生成してアクセスするものは、インスタンスメソッド、インスタンスフィールドと呼びます。こちらはインスタンス化が必要です。
クラスメソッドからインスタンスメソッドやインスタンスフィールドにアクセスすることはできませんので注意が必要です。(※インスタンスクラスからクラスメソッド、クラスフィールドにアクセスすることは可能です。)
Step2: 使い方を知る
static を付ける、または付けないという違いを知ってそれぞれを使い分けることができると、コーディングの幅が広がります!!
- クラス内の変数(クラス変数)
「static変数」は「クラス名.」の後に記述しますので 「クラス変数」 - インスタンス固有の変数(インスタンス変数)
「非static変数」は「インスタンス名.」の後に記述しますので「インスタンス変数」
static変数
static変数(クラス変数) は以下のように宣言します。
宣言したクラス変数を呼び出す場合は以下のようにします。
staticメソッド
staticメソッドは、クラスに対してただひとつのメソッドです。
staticメソッドは、そのクラスでどんなにインスタンスの生成をしてもただひとつのメソッドを指します。
インスタンスフィールドはインスタンスごとに値を保存することができますが、クラスフィールドはインスタンスを複数生成しても値が共有されるのでただ一つのクラスフィールドにアクセスするようになっています。
そのため、実はインスタンスを生成しなくてもメソッドを呼び出すことが可能です。
「クラス名.メソッド名()」という形式で staticメソッドを呼び出せます。
(※逆に、クラスフィールド・メソッドをインスタンスを生成した上で呼び出すと、その必要が無いためワーニングが表示されます。)
補足
「ただひとつのメソッドを指します。」 と上述していますが、ピンとこない方もいるかと思いますので補足です。
世界には同姓同名の人々が多数存在しますよね。
しかしながら、あなたは世界に一人だけです。
例えば、あなたが佐藤さんだとして、男性なのか女性なのか。
あるいは、 東京住みなのか、神奈川住みなのか。
そういった違いがありますよね。
「インスタンスの同一であるとか、 ひとつであるとか」のイメージって、 上記に近いものがあります。
簡単にですが、staticについて学ぶ前にインスタンスについて復習 & 必要知識を伝授していきます !
サンプル用のJavaプログラムを作成していきます。
1. インスタンスを生成するためのクラスの作成
下準備として以下のコードを用意しておきます。
※今回は「佐藤さん」を題材にします。
2. Eclipseメニューにある自動生成機能を使って必要なメソッドを揃える
以下の画像のように 「hashCode() および equals() の生成…」を選択します。
※該当クラスにフィールド変数がある状態で実施すること
3.黄枠のチェックボックスを全てチェックして生成ボタンを押下
※自動生成するメソッドの挿入位置に関しては自由ですので、ドロップダウンリストに表示されたままの状態でもOKです。
4. 自動生成完了後の内容
黄枠が自動生成されたメソッドになります。
サンプル
フィールド変数のハッシュコードを元に、インスタンスの同一性をチェックするクラスです。
(コピーできるようコードとしても残していきます。)
サンプル: Main
上記クラスのインスタンスを使用するために、 最低限のmainメソッドを実行できるクラスを用意します。
【出力結果】
・hashCode() にて対象の値のハッシュコードを取得する
Javaにおけるハッシュコードは、簡単に言えば、対象の値を整数値に変換したデータになります。
(※「パスワード等に用いられるハッシュ」とは少々異なるため、ここではインスタンスに対するハッシュの認識でいてください。)
hashCode() によって、インスタンスを生成後の sato1, 2, 3 のチェックしてみたところ、
sato1, 3 は同一のハッシュコード (680786 ) であることが見て取れましたね。
ここで呼び出した hashCode() は、 InstanceOfSatoクラスの自動生成されたメソッドを指しています。
実際の動きとしては、 InstanceOfSatoクラスのフィールド変数であるfirstnameがhashCode()の評価対象となり、 sato1, 2, 3 を比較したところで、異なるインスタンスを生成していることは明白であるため、フィールド変数である firstName を比較の軸として利用しているわけです。 firstnameのハッシュコードを計算して戻り値として返しています。
・equals() メソッドにてインスタンスが等価であるかのチェックを実施
equals() メソッドも何度か目にしているかと思いますが、 Stringクラスの文字列を比較する際にequals()を使用する理由はまさにこういうことなのです。
String は元々 Object (参照型のクラス) を継承しているため、単なる値の比較で用いるのは上手く機能しない場合があります。(== を用いる対象としては、 値型である int や boolean などが適切ですね。)
長くなりましたが、 上記を踏まえてstaticが 「ただひとつである」ということを再度見ていきましょう。
サンプル: staticな変数の値の動き
【InstanceOfSato】 へ以下の内容を追記します。
【Main】には以下を追記します。
【出力結果】
・非static と static の性質の差
動的に使用した場合には、その差がハッキリと見受けられます!
今回は生成された sato1, 2, 3 のインスタンス変数より、フィールド変数とクラス変数に対し、それぞれをインクリメントした後の値を出力しています。
・非staticなフィールド変数である id
→インスタンスを生成する度に初期化されるため、インクリメント後の出力値は常に1
・staticなクラス変数 id
→ InstanceofSatoクラスにおいて一意である
→ incrementId() メソッドの呼び出し元 (sato1,2,3など) が異なっていても、 同じ id が使用されている
→同一の値を使用するため、インクリメント後の出力値は、incrementId() メソッドを使用する回数に比例する
出力結果の動きや解説を見て、「ただひとつ」ってそういうことなのか〜! と理解の助けになれば幸いです!
注意点
1. staticメソッド内で扱う変数は、全てクラス変数かローカル変数 (メソッド内で宣言した変数) でなければなりません。
2. staticメソッドは、サブクラスに継承されず、 オーバーライドすることもできません。
どの内容にも言えることですが、 特に final や static に関してはなんとなくで使用せずしっかりと使用するシチュエーションを見極めて使えるようになると、 無駄の無い効率的な開発が行えるようになります!
本章に関しては課題はありませんが、 サンプルコードを参考に実際に自分の手でコードを記述し、 実行し、結果を確認してみましょう!
