インフラ屋が改めてJavaをお勉強する（第１回：クラス設計）

１０年以上前に研修で３ヶ月程度Javaを習っただけのインフラ屋が、Java Gold目指してお勉強しますというお話。携帯の長期契約特典で付与されたポイントで購入した[この本](https://books.google.co.jp/books?id=N0ktDQAAQBAJ&printsec=frontcover&dq=java+gold&hl=ja&sa=X&ved=0ahUKEwjO7_LEv5PZAhXFKZQKHeI-AIQQ6AEIQDAE#v=onepage&q=java%20gold&f=false)をベースに要点を自分なりにまとめていきます。第一回はクラス設計。

# カプセル化
+ クラスのフィールドに対するアクセス権を適切に設定しましょうというお話
+ メンバー変数を`private`修飾子つきで宣言する
+ アクセッサと呼ばれる`public`なメソッドを介してメンバー変数へのアクセスを許可する

```java:例
class Hoge {
    private int hoge;
    public Hoge(int hoge) { this.hoge = hoge; }

    /*accessor*/
    //setter
    public void setHoge(int hoge) {
        //check
        if(check) this.hoge = hoge;
    }
    //getter
    public int getHoge() { return hoge; }
}
```

`private`以外のアクセス修飾子とその効果について次にまとめ。

## アクセス修飾子とスコープ

| 修飾子（Access Modifiers） | クラス | パッケージ | サブクラス | その他（全て） |
|:--|:-:|:-:|:-:|:-:|
|public|o|o|o|o|
|protected|o|o|o|x|
|指定なし（デフォルト）|o|o|x|x|
|private|o|x|x|x|

[参照ドキュメント](https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html)

## [Class Modifiers](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.1)
（Class）修飾子のまとめ。

### abstract
不完全（と考えるべき）クラスであることを示す。abstractクラスからインスタンスを生成しようとするとコンパイル時にエラー。メソッドに付与した場合は未実装であるということ。abstractメソッドはabstractクラスにしか定義（宣言）できない（普通のクラスにabstractメソッドを書いたらコンパイルエラー(a)）。逆に、abstractメソッドを持つabstractクラスを継承して、かつそのabstractメソッドをオーバーライド（実装）しないなら、そいつもabstractクラスでないとダメ（コンパイルエラー(b)）。  
~~なお、インタフェースを実装（implements）したabstractクラスに、インタフェースで宣言されたものと同じメソッドシグネイチャーを持つabstractメソッドを宣言してもコンパイルエラー~~[saka1029様コメントでのご指摘により2018.02.11修正]インタフェースを実装したabstractクラスで、メソッドを実装（@Override）せずにabstractのままにしておくことは可能だが、可視性を下げられないためpublicを入れ忘れるとコンパイルエラー(c)。

```java:コンパイルエラー(a)
abstract class Hoge {
    abstract void doHoge();
}
class Hoge2  {
    public void doHoge2() {
        Hoge hoge = new Hoge();  //abstractクラスをインスタンス化しようとしているのでコンパイルエラー
    }
}
```

```java:コンパイルエラー(b)
abstract class abstractHoge {
    abstract void doHoge();
}
class Hoge extends abstractHoge {
    public void hoge() { } //abstractメソッド（doHoge()）が残っているのにabstractにしていないのでコンパイルエラー
}
```

```java:コンパイルエラー(c)
interface HogeInterface {
    void doHoge(int hoge);
}
abstract class abstractHoge implements HogeInterface {
    abstract void doHoge(int hoge);    //インタフェースメソッドとシグネイチャーが被っているabstractメソッドを宣言しているのでコンパイルエラー　→ saka1029様よりコメントでのご指摘[2018.02.11訂正]シグネイチャーが被っていても、それはただ単にimplemetsしているものの相変わらず未実装（abstract）なままの宣言というだけであり、コンパイル可能。interfaceで宣言されたメソッドは暗黙的にpublicであり、ここではpublicがついていないので、インタフェースで宣言されたメソッドの可視性を下げることはできないというコンパイルエラー 
}
```

### final
ほぼそのまんまだがこれ以上継承できないクラスであることを示す。finalクラスをextendsしようとしたらコンパイルエラー(a)。abstractとfinalを同時に使ったらコンパイルエラー（extendsすることが前提のabstractとextendsできないfinalを同時に使ったら明らかに矛盾）(b)。

```java:コンパイルエラー(a)
final class finalHoge { }
class SubHoge extends finalHoge { } //finalクラスをextendsしているのでコンパイルエラー
```

```java:コンパイルエラー(b)
abstract final class OmaenanishitekuretennoHoge { } //abstractでfinal!?なのでコンパイルエラー
```

### strictfp
strictなFP（Floating Poing）。つまり厳格な浮動小数点。なんだそれ？算数系は苦手なので、ざくっと丸めてまとめると、このへんの小数点の扱いはIEEEあたりが標準を制定しているが、strictfpな世界ではfloatはfloat、doubleはdoubleで中間値も含めて厳格に扱われる（暗黙の型変換とかしない）が、strictfpでないコンテクストでは丸め誤差（underflow）や桁落ち（overflow）をある程度許容するよ、というお話らしい（[参考](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.4)）。どうやら32bitと64bit計算とで浮動小数点の有効桁数の違いからくる結果の差をなくすような働きがあるらしい。。。見たことないからよくわからん。

# 継承
クラスをextendsすることで、以下を継承する。

+ フィールド
+ メソッド
+ 内部クラス
+ インタフェース
+ 列挙型

Javaでは複数の同時継承は不可。C++だといけるらしい。

## is-a関係とhas-a関係
BがAをextendsしていたら、B "is-a"というだけのお話。Javaでは全てのクラスの親がObjectなので、全てのクラスはObjectクラスである。
has-aはあるクラスの中に別のクラスがインスタンス変数として入っているというだけ。これにはさらに二種類あって、「集約（aggregation）」の場合は、関係するオブジェクトがライフサイクルを共にしない。「構成集約（composition）」のときはライフサイクルを共にする（要はB has aのとき、Bがdestoyされたらaもdestroyされる）。

```java:aggregation
package net.mognet.java8gold;
class A {
	public A() {
		System.out.println("A is created.");
	}
	@Override
	protected void finalize() throws Throwable {
		System.out.println("A is finalized.");
	}
}
class B {
	private A a;
    public B(A a) {
    		this.a = a;
    		System.out.println("B is created.");
    }
    
    @Override
    protected void finalize() throws Throwable {
    		System.out.println("B is finalyzed.");
    }
}
public class Test {
	public static void main(String[] args) throws InterruptedException {
		A a = new A();
		B b = new B(a);
		b = null;
		System.gc();  //bはfinalizeされるがaはfinalizeされない
	}
}

```

```java:composition
class A {
	public A() {
		System.out.println("A is created.");
	}
	@Override
	protected void finalize() throws Throwable {
		System.out.println("A is finalized.");
	}
}
class B {
    A a = new A();
    public B() {
    		System.out.println("B is created.");
    }
    
    @Override
    protected void finalize() throws Throwable {
    		System.out.println("B is finalyzed.");
    }
}
public class Test {
	public static void main(String[] args) throws InterruptedException {
		B b = new B();
		b = null;
		System.gc();   //これでAもBもfinalize
	}
}
```

# ポリモーフィズム
ざっくりいえば、親の型を使って子のオブジェクトを作れまっせ、ということ。

```java
class A {}
class B extends A {}
class C {
    private A a = new B(); //OK
}
```

もっと極端には、全てのスーパークラスがObjectなので、こうもかける。

```java
class A {}
class B extends A {}
class C extends B {}
class Test {
    private Object a = new A(); //OK
    private Object b = new B(); //OK
    private Object c = new C(); //OK
}
```

コンパイル時に具体的な型がわからなくても、提供する共通のメソッドがわかっていれば、親のクラスの型にしておいて、そのメソッドを呼び出すコードを書ける。実際の振る舞い（メソッドの実装）は、実行時に決まる。

# [Objectクラスのメソッド](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Object.html)

|メソッド|内容|
|:--|:--|
|boolean equals(Object obj)|オブジェクトと渡されたobjが等しいかを返す|
|protected void finalize()|gc実行時にコールされる|
|Class<?> getClass()|実行時クラスを返す|
|int hashCode()|オブジェクトのハッシュ値を返す|
|void notify()|１スレッドを再開する|
|void notifyAll()|全スレッドを再開する|
|String toString()|オブジェクトを文字列化して返す|
|void wait()|notify()されるまで待つ|

# staticなやつら
## [static fields](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.1.1)
staticな変数は、インスタンスがいくつできようとも一つしか物理的に（メモリ上に）存在しない。

```java:staticFields
class A {
    private static int i = 0;
    public static void add(int a) { i = i + a; }
    public static int getI() { return i; }
} 
public class Test {
	public static void main(String[] args) {
		A[] a = { new A(), new A(), new A(), new A() }; //いくつインスタンスを作ろうとも
		a[0].add(3);
		System.out.println(a[1].getI());  //３が返る
		a[2].add(4);
		System.out.println(a[3].getI());  //７が返る
	}
}
```

static fieldsにはstaticなやつらしかアクセスできない。間違ってもこんなことをしてはダメ（コンパイルエラー(a)）。

```java:コンパイルエラー(a)
class A {
    private static int i;
    public A(int i) {
        this.i = i;    //static fieldにnon-staticアクセスでコンパイルエラー
    }
}
```

別名、クラス変数などとも言う通り、インスタンス変数ではないので、"this.i"のようなアクセスはできない。というかむしろ、インスタンス化しなくても使える。

```java:staticFields2
class A {
    private static int i = 0;
    public static void add(int a) { i = i + a; }
    public static int getI() { return i; }
} 
public class Test {
	public static void main(String[] args) {
		System.out.println(A.getI());  //0が返る
	}
}
```

## [static methods](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.3.2)
別名クラスメソッド。thisとかsuperとかインスタンス前提のキーワードを使ったらもうコンパイルエラー(a)。なお、逆はもちろんOK。

```java:コンパイルエラー(a)
class A {
    private static int i = 0;
    private int memberField = 0;
    public void memberMethod() { //do something with i } //OK
    public static void classMethod() { //do something with this.memberField } //staticメソッドからメンバー変数を参照しているのでコンパイルエラー
}
```

## [static initializer](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.7)
クラスがロードされたとき、つまりJVMが立ち上がったときに実行されるコード。主にstatic fieldsの初期化なんかに使えるが、そのほかに振る舞いも書ける。static initializerのブロック外で宣言したフィールドにアクセスしたり、staticコンテクストではご法度のthisとかsuperとかやりだしたらコンパイルエラー(a)。

```java:コンパイルエラー(a)
clss A() {
    private int i;
    private static int j;
    static {
        this.i = 1;    //インスタンス変数にアクセスしているのでコンパイルエラー
        j = 1;         //OK
        static int k = 1;  //static initializer内で重ねてstaticつけているのでコンパイルエラー（くどい！）
    }
}
```

# Singletonパターン
あるクラスのインスタンスがただ一つであることの保証をベースにした設計パターン。

+ private staticフィールドを宣言して自身の単一のインスタンスを生成することで、常にその一つのインスタンスのみが参照できるようにする
+ コンストラクタをprivateで宣言し、他からインスタンスを生成できないようにする
+ private staticにした自身のインスタンスを返すpublic staticなアクセッサを定義する

```java:Singleton
public class SingletonHoge {
    private static SingletonHoge shoge = new SingletonHoge();
    private SingletonHoge() {}
    public static SingletonHoge getInstance() { return shoge; }
}
```

# 不変クラス（Immutable Class）
一度インスタンス化されたオブジェクトにおいて、その内容（フィールド）が更新されないように設計されたクラス。

+ クラスはfinalにしておく
+ フィールドはprivate finalにしておく
+ アクセッサはgetterのみとする
+ どうしても保持する値を変更したい場合は、更新したフィールドを持つ新しいインスタンスを返す

```java:Immutable
public final class ImmutableHoge {
    private final int hoge;
    private final int[] hogearray = { 1, 2, 3 };
    public ImmutableHoge(int hoge) { this.hoge = hoge; }
    public int getHoge() { return this.hoge; }
    public ImmutableHoge add(int hoge) { new ImmutableHoge(this.hoge + hoge); }
}
```

# abstract, static, and default methods
Java 8からインタフェースに実装メソッドを書けるようになった。

```java:Interface
interface Hoge {
    void doHoge(); //abstract
    static void staticDoHoge() {
        //do something in static context
    }
    default void defaultDoHoge() {
        //do something
    }
}
```

ただし、インタフェースのstaticメソッドはインタフェースへの参照からしか呼出せない。

```java:staticMethod
interface InterfaceHoge {
    static void doHoge() { System.out.println("hoge"); }
}
class ClassHoge implements InterfaceHoge {
    ClassHoge choge = new ClassHoge();
    InterfaceHoge.doHoge();    //OK
    choge.doHoge();            //実装クラスインスタンスから呼出しているのでコンパイルエラー
    ClassHoge.doHoge();        //実装クラスへの参照から呼出しているのでコンパイルエラー
}
```

# [入れ子クラス](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#d5e11140)
ブロック内で宣言されたクラスのこと。

1. クラスメンバーの入れ子クラス（Inner Class）
   + メンバー・クラス（非staticメンバー・クラス）：クラスメンバーとして宣言された入れ子クラス
   + ローカル・クラス：メソッド内で宣言された入れ子クラス
1. staticメンバー・クラス：クラスメンバーとしてstatic宣言された入れ子クラス

なお、インタフェース内のメンバークラスは全てstatic扱いとなるので、内部クラスとは扱わない（staticメンバー・クラスになる）。

+ 内部クラスはstatic initializerを使えない（コンパイルエラー）
+ 内部クラスではstaticフィールドを使えない（コンパイルエラー）ただしfinalした定数は可

```java:NestedClass
public class OuterHoge {
    static class InnerStaticHoge { }   //staticメンバー・クラス
    private class InnerPrivateHoge { } //メンバー・クラス（非staticメンバークラス）
    void doHoge() {
        class HogeInMethod { }         //ローカル・クラス
    }
}
```

呼出し方はこうなる。

```java
public class OuterHoge {
    public void doHoge() {
        InnerHoge ih = new InnerHoge();
        ih.doInnerHoge();
    }
    public class InnerHoge {            //メンバー・クラス
        public void doInnerHoge() { }
    }
}
```

staticコンテクストからの呼出しはややこしいが、Outerクラスのインスタンスを生成した上で、そのオブジェクト参照からnewする。

```java
public class OuterHoge {
    public static void doHoge() {
        OuterHoge oh = new OuterHoge();    //自分（Outerクラス）をインスタンス化
        InnerHoge ih = oh.new InnerHoge(); //Outerクラスへの参照からのnew
        ih.doInnerHoge();
        //InnerHoge ih = new OugerHoge().new InnerHoge(); //１行にまとめるとこうなる
    }
    public class InnerHoge {
        public void doInnerHoge() { }
    }
}
```

他のクラスからの呼出し。

```java
public class AnotherHoge {
    public void doAnotherHoge() {
        OuterHoge oh = new OuterHoge();
        OuterHoge.InnerHoge ih = oh.new InnerHoge();
        ih.doInnerHoge();
    }
}
```

# [匿名クラス（Anonymous Class）](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.9.5)
コンパイラによってクラスのインスタンス生成時に自動的に作成される。
匿名クラスはabstract不可、暗黙的にfinalであり、常に非staticな内部クラスになる。実際にはinterfaceの実装を提供するために使う（ということはinterfaceにdefaultメソッドを定義できるようになった今は不要？かとおもいきや、実際にはいろんなところでいろんな軽い実装をするようなケースで使う）。

```java
interface Hoge {
    public void doHoge(); //abstractメソッド
}
class HogeClass {
	void hogeMain() {
            Hoge hoge = new Hoge() {
        	@Override
        	public void doHoge() { }
        };
        hoge.doHoge();
	}
}
```

要は、わざわざinterfaceを実装（implements）したクラスを作ってメソッドをOverrideしなくても、その場でインタフェース型のオブジェクトを作り、そのなかに匿名クラスを作ることでメソッドを実装できる、ということ。

ワンライナーで書くとこうなる。

```java
new Hoge() {
    public void doHoge() {}
}.doHoge();
```

ここまでくるとなんだかなぁ。。。MySQLのDataSourceプロパティを調べていた時に、ドライバのソースコードを読んでいたら、内部クラスがいたるところに出てきて何をどう呼出していいのか混乱したことが記憶に新しいが、この辺を使いこなしてこそなのかも。

# [列挙型](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9)
関連する定数（暗黙的に`public static final`）を一つにまとめるために使う。
構文は`modifier enum Name { Const1, Const2, ... }`となる。

```java
public enum DogType {
    BORDER_COLLIE, GOLDEN_RETRIEVER, AKITA; //他にフィールド、メソッドを宣言するならそれより前に定数を書く
    private int hoge;        //フィールドも宣言できる
    public void doHoge() { } //メソッドも宣言できる
}
```

クラスやインタフェースと同様、トップレベルまたは入れ子の型として宣言でき、暗黙的に`final`クラスとしてコンパイルされる（継承（exntends）不可）。入れ子クラスと異なり、メソッド内には宣言不可。
全ての列挙型は暗黙的に`java.lang.Enum`のサブクラスとしてコンパイルされるので、何かを継承（extends）するのは不可だが、interfaceを実装（implements）することは可能。ネストの列挙型は暗黙的に`static`となる（明示してもよい）。
`java.lang.Enum`クラスが提供するメソッド。
<dl>
<dt>`String final name()`</dt>
<dd>列挙定数名を文字列として返す</dd>
<dt>`int ordinal()`</dt>
<dd>列挙定数の序数を返す</dd>
<dt>`static <T extends Enum<T>> valueOf(Class<T> type, String name)`</dt>
<dd>第一引数に指定した列挙型から、第二引数に指定した名前の列挙定数に一致した列挙定数オブジェクトを返す</dd>
<dt>`static E valueOf(String name)`</dt>
<dd>指定した名前の列挙定数に一致した列挙定数を返す</dd>
<dt>`static E[] values()`</dt>
<dd>列挙型の配列を返す</dd>
</dl>

```java:EnumTest
enum DogTypes {
	BORDER_COLLIE, BULL_DOG, GOLDEN_RETRIEVER;
	private int i = 0;
	public void printField() { System.out.println(i); }
}
public class EnumTest {
	public static void main(String[] args) {
		System.out.println(DogTypes.BORDER_COLLIE);
		for(DogTypes dt : DogTypes.values()) {
			dt.printField();
			System.out.println(dt + ": " + dt.name());
		}
		System.out.println(DogTypes.valueOf("BORDER_COLLIE"));
	}
}
```

```text:実行結果
BORDER_COLLIE
0
BORDER_COLLIE: BORDER_COLLIE
0
BULL_DOG: BULL_DOG
0
GOLDEN_RETRIEVER: GOLDEN_RETRIEVER
BORDER_COLLIE
```

## コンストラクタ
privateなコンストラクタのみ可。引数なしは不可。Singletonかつ不変オブジェクトとすることが要件なので、オブジェクト生成時にフィールド値を指定する（setterは提供せず、getterのみ）。

```java
enum DogTypes {
    NA, SMALL(5), MEDIUM(10), LARGE(20);      //引数なしコンストラクタ、引数それぞれ5, 10, 20のコンストラクタ呼出し用
    private int weight;
    private DogTypes() { this.weight = 1; }
    private DogTypes(int weight) { this.weight = weight; }
    public int getWeight() { return this.weight: }
}
class Hoge {
    public void doHoge() {
        DogTypes dt = DogTypes.MEDIUM;
        System.out.println(dt.getWeight());
    }
}
```

今回はここまで。