静的メンバ
頭にstatic
を入れるだけ。
下記の例は複数の人間で一つの財布を持っているようなイメージ。
この場合、それぞれのHumanインスタンスh1
,h2
がフィールドを持っているのではなく、Human
クラスがフィールドを持っているという考え方。
なので静的フィールドはクラス変数とも言われる。
public class Human{
static int money=100;
}
public class Main{
public static void main(String[] args){
// 一つ目のインスタンスを作成
Human h1 = new Human();
// 二つ目のインスタンスを作成
Human h2 = new Human();
// h1.moneyクラス変数を書き換え
h1.money=200;
// h2.money変数を表示
System.out.println(h2.money);
// moneyはクラス変数だから200と表示されるはず
}
}
静的メソッドはインスタンスを生成しなくても呼び出せる。
だから、上の例で言うと、いきなり
System.out.println(Human.money);
とか書いても大丈夫。
main
メソッドが静的メソッドなのもそういう理由。
カプセル化
カプセル化を実現する一つの方法として、アクセス修飾子をクラスやメンバに付けて可視性を設定する。
-
public
:すべてのクラス -
protected
:自分と同じパッケージに属するか、自分を継承したクラス -
packaged private
(デフォルト):自分と同じパッケージに属するクラス -
private
:自分自身のクラスのみ
public class Test{
private int i=1;
public void calc(int j){
return this.i+j;
}
}
getterとsetter
フィールドはprivate
、メソッドはpublic
に設定し、getterとsetterで値を受け渡す方法の基本的な書き方。
public class Test{
private int num;
public int getNum(){
return this.num;
}
public void setNum(int num){
this.num = num;
}
}
継承・実装
- 継承:
exetends
- 実装:
implements
抽象クラスの頭に付けるabstract
が取れるのは抽象部分がオーバーライドされ尽くした時。
継承(exetends)
ここではInu
← Shibainu
という継承関係。
なお、osuwari
メソッドは抽象化されているのでShibainu
クラスでオーバーライドされる必要がある。
public abstract class Inu{
public void ote(){
System.out.println("左手を差し出す");
}
public abstract void osuwari();
// ここは何も書かない
}
public class Shibainu extends Inu{
public void osuwari(){
System.out.println("地面に座る");
}
public void mate(){
System.out.println("ご飯を我慢する");
}
}
public class Main{
public static void main(Strgin[] args){
Shibainu dog = new Shibainu();
dog.ote(); // Inu由来メソッド
dog.osuwari(); // Inu(←Shibainu)由来メソッド
dog.mate(); // Shibainu由来メソッド
}
}
多態性を利用して、
Inu dog = new Shibainu();
dog.osuwari();
ということもできる。
この場合、呼び出すことのできるメソッドはInu
クラスが持っているものだけになる。
ゆえに、実際に生成されるインスタンスがShibainu
でもdog.mate
は呼び出せない。
osuwari
メソッドが呼び出された場合は、Inu
の方ではなく、Shibainu
の方のメソッドが処理される。
こうした多態性を利用して、以下のような処理も書ける。
Inu[] dogs = new Inu[3];
Inu[0] = new Shibainu();
Inu[1] = new Akitaken();
Inu[2] = new Tosaken();
for (Inu d: dogs){
d.osuwari();
}
「地面に座る」「寝る」「言うことを聞かない」など、同じmate
メソッドでもクラスによってメソッドの中身が違う場合にとても便利。
継承に関してはコンストラクタに注意する必要がある。
super
を使って明示的に引数を与えてやらないといけない時があるからだ。
ここで使うsuper
は子クラスのメソッド内で親クラスのメソッドを呼び出すときのsuper
とは別物と認識していた方がいい(これはthis
でも同じ)。
実装(implements)
クラスをインターフェイスにできる条件は2点。
- すべてのメソッドが抽象メソッドであること。
- 基本的にフィールドを一つも持たない。
「基本的に」とあるのは、public static final
が付いたフィールド(定数)だけは宣言が許されるため。ただその場合はpublic static final
は省略可にしてもいい。
class
ではなく、interface
と宣言する。
public interface Animal{
void run();
}
public class Neko implements Animal{
void run(){
System.out.println("4本足で走る");
}
}
インターフェースは多重実装が許されるので
public calss Neko extends Mammal implements Animal, Organic{
// MammalクラスとAnimalインターフェースとOrganicインターフェースを親に持つ。
}
こういう書き方もできる。
猫クラスは哺乳類クラスを親に持ち、動物と有機物のインターフェースを持つ、という感じ。あんまり上手い例ではない。
インターフェース・抽象メソッドの美点は
- オーバーライドを強制させられる。
- 意図しないインスタンス化が避けられる。
- 何もしないメソッドと明確に区別できる。
といったところ。
参考書籍
スッキリわかるJava入門第二版
Pp.332-524