【Java】オブジェクト指向構文 - クラスメソッド/引数

#クラスメソッド/フィールド
* インスタンス生成しなくても**クラスから直接呼び出せるフィールド/メソッド**
* **静的メソッド / staticメソッド**ともいう
* **クラスメンバー**
    * クラスメソッド
    * クラスフィールド
* クラスメンバーはクラスで１つだけの存在、全インスタンスで共有
* cf: **インスタンスメンバー**：オブジェクト経由で呼び出すメンバー
    * それぞれが独立した値（実態）を持つ

##クラスメソッド
* **staticをつけるだけ**
* 注意
    * **クラスメソッドではthisは使えない**
        * インスタンスそのものが生成されてない
        * クラスメソッドからインスタンスフィールド/メソッドは参照できない
    * **クラスメソッドはオブジェクト経由で呼ばない**

```java
//FigureクラスのクラスメソッドgetTriangleAreaを定義
public class Figure {
  public static double getTriangleArea(double width, double height) {
    return width * height / 2;
  }
}
```

```java:StaticBasic.java
public class StaticBasic {

  public static void main(String[] args) {
    System.out.println(Figure.getTriangleArea(10, 20));
//    var f = new Figure();
//    System.out.println(f.getTriangleArea(10, 20));
  }
}
```

###ユーティリティクラス
* クラスメンバーしか持たないクラスのこと（Mathクラスなど）
* 関連した機能を１つのクラスメソッドでまとめることで、可読性をあげ機能を探しやすくする
    * Mathクラスでは数値関連の処理を束ねている
* **コンストラクタをprivate化(外から呼べない)することでクラスのインスタンス化を禁止**できる
* `var m = new Math(); //エラー`
* クラスの継承を明示的に禁止するために**final修飾子**を使うのが一般的

```java
public final class Math{
  private Math() {}
}
```
##クラスフィールド
* インスタンス介さず直接呼べるフィールド
* **staticをつけるだけ**
* クラスフィールドはクラスメソッドからアクセスできる
    * クラスフィールドはクラスに属するから
* アクセス権限を満たせば外部からもアクセス可能
* 注意：クラスフィールドは**内容を変更した場合関係するインスタンスに全部影響**してしまう
* 他の言語でいう**グローバル変数**：プログラム全体で有効な変数
* **クラス間の依存関係が強くなってしまう**ので原則以下条件で使う
    * 読み取り専用
    * クラス自体の状態監視
        * **ex: シングルトンパターン**(下記参考)
   

```java:Figure.java
//Figureクラスのクラスメンバーにpiフィールド、getCircleAreaメソッド定義

public class Figure {
  public static double pi = 3.14;

  //クラスメソッドからクラスフィールドにアクセス
  public static void getCircleArea(double r) {
    System.out.println("円の面積は" +  r * r * pi);
  }
}
```
```java:StaticBasic.java
public class StaticBasic {
  public static void main(String[] args) {
    //外部からもFigureにアクセス可能
    System.out.println(Figure.pi); //3.14
    Figure.getCircleArea(5); //円の面積は78.5
  }
}
```

###シングルトンパターン
* デザインパターンの一種
* ある**クラスのインスタンスを一個しか生成しない/したくない**時に使う
    * **コンストラクタをprivate宣言**
    * アプリで保持すべき**唯一のインスタンスをクラスフィールドで保存**
        * **クラスロードの初回のみ一度インスタンス生成**、以降インスタンス生成できない
    * 唯一の**インスタンス取得にクラスメソッド**（=インスタンス取得のためのファクトリーメソッド）使う
    * インスタンスそのものの管理/生成クラスに属する役割なのでクラスメンバーとして定義

```java
public class MySingleton {

  //唯一のインスタンスをクラスフィールドで保存
  private static MySingleton instance = new MySingleton();

  //コンストラクタをprivate宣言
  private MySingleton() {}
  //クラスメソットで用意していたインスタンス取得
  public static MySingleton getInstance() {
    return instance;
  }
}
```

##クラス定数
* **class{...}直下で定義された定数、読み取り専用のフィールド**
* **final修飾子つけるだけ**
* cf: 定数クラス
    * アクセスしやすさのためアプリ全体で利用する定数を1つのクラスにまとめているクラスのこと(まず関連するクラスで定義するのが基本)

```java
public class MyApp {
  public static final String BOOK_TITLE = "Javaマスター！";
}
```

```java
public class ConstantBasic {
  public static void main(String[] args) {
    System.out.println(MyApp.BOOK_TITLE); //Javaマスター！
    //finalフィールドには代入できない
    //MyApp.BOOK_TITLE = "Javaマスターに俺はなる！"; //エラー
  }
}
```

##static初期化ブロック
* **クラスが初期化される際に実行**
    * cf: 初期化ブロックはインスタンス生成時に実行
* Static Initializer
* **ブロック先頭にstatic修飾子**
* クラスメンバーを初期化するのに使う
    * 定数はコンパイル時に参照元のクラスにコピーされる
        * 値を修正した場合、定数参照クラスも再コンパイル必要
        * 参照元クラスのサイズが少し大きくなる
    * →static初期化ブロックで初期化することで**定数値しかコピーできないので解決！**

```java
public class Initializer {
  public static final String DOG;
  public static final String CAT;
  public static final String MOUSE;

  static {
    DOG = "inu";
    CAT = "neko";
    MOUSE  = "nezumi";
  }
}
```

##初期化の実行順序
**（1）クラス初期化の一回のみ実行**

* クラスフィールドに規定値を設定
* クラスフィールド初期化
* static初期化ブロック実行

**（2）オブジェクト生成時に毎回実行**

* インスタンスフィールドに規定値を設定
* インスタンスフィールドを初期化
* 初期化ブロック実行
* コンストラクタ実行

```java
//よくない例
//フィールド初期化子より初期化ブロックが優先される
public class Order {
  //フィールド初期化子
  String value = "First!";
  //コンストラクタ
  public Order() {
    System.out.println(value); //Second!!
  }
  //初期化ブロックはコンストラクより先に実行
  {
    value = "Second!!";
  }
}
```
```java
public class OrderBasic {

  public static void main(String[] args) {
    var o = new Order();
  }
}
```

#可変長引数のメソッド
* **引数に`...`を付与するだけ**
* 受け取った値は配列とみなす

```java
public class ArgsParams {
  //引数に...で可変長引数を受け取る
  public int totalProducts(int... values) {
  // public int totalProducts(int[] values) {
    var result = 1;
    //拡張forで引数values受け取りresultにかける
    for (var value : values) {
      result *= value;
    }
    return result;
  }
}
```
```java
public class ArgsParamsBasic {

  public static void main(String[] args) {
    var v = new ArgsParams();
    System.out.println(v.totalProducts(12, 15, -1));
    System.out.println(v.totalProducts(5, 7, 8, 2, 4, 3));

    // System.out.println(v.totalProducts(new int[] {12, 15, -1}));
  }
}
```

* 注意
    * **メソッドに1つ、引数リストの末尾にのみ指定可能**
        * NG例
            * `public void hoge(int... x, int y)`
            * `public void hoge(int... x, int... y)`
    * **想定される引数を可変長引数にまとめない**
        * 通常の引数が基本、可変長はメソッド定義時点で個数を特定できない情報のみまとめる
    * **可変長引数で一個以上の引数を表現できる**
        * 可変長引数は0個以上の値を要求する引数
        * 可変長引数の省略も可能
        * `v.totalProducts()` の場合引数にサイズ0の配列が渡される
        * 最低一個の引数を要求する場合以下のコード

```java
//1個目の引数は普通のinitial
//2個目以降の引数を可変長で宣言
//引数なしの場合コンパイルエラー
public class ArgsParamsGood {
  public int totalProducts(int initial, int... values) {
    var result = initial;
    for (var value : values) {
      result *= value;
    }
    return result;
  }
}
```
```java
public class ArgsParamsGoodClient {
  public static void main(String[] args) {
    var v = new ArgsParamsGood();
    System.out.println(v.totalProducts()); //no arguments
  }
}
```
##引数とデータ型
* 基本型、参照型で受け渡した値への影響範囲が異なる

* 基本型

```java
public class ParamPrimitive {
  public int update(int num) { //仮引数num
    //仮引数の操作
    num *= 10;
    return num;
  }
}
```
```java
public class ParamPrimitiveBasic {
  public static void main(String[] args) {
    //変数num
    var num = 2;
    var p = new ParamPrimitive();
    System.out.println(p.update(num)); //20
    //元の変数numには影響しない
    System.out.println(num); //2
  }
}
```

* 参照型
    * **値ではなくメモリ上の場所を格納**

```java
public class ParamRef {
  //変数dataの参照アドレスを仮引数dataにコピー
  public int[] update(int[] data) { //仮引数data
    //仮引数dataの操作
    data[0] = 5;
    return data;
  }
}
```
```java
public class ParamRefBasic {

  public static void main(String[] args) {
    //実引数data
    var data = new int[] { 2, 4, 6 };
    var p = new ParamRef();
    //メソッド呼び出しで実引数、仮引数とは同じ値を参照することになる
    System.out.println(p.update(data)[0]); //5
    //仮引数dataの操作は元の変数に影響
    System.out.println(data[0]); //5
  }
}
```

* 参照型でも**参照先そのものを置き換えたら元の変数に影響しない**
    * ex: 配列そのものを置き換えた場合

```java
public class ParamRefArray {
  //メソッド呼び出し時点で実引数、仮引数は同じものを指す
  public int[] update(int[] data) {
    //新たに配列代入した場合、参照先アドレスそのものが変わる
    data = new int[] { 10, 20, 30 };
    return data;
  }
}
```

```java
public class ParamRefArrayBasic {
  public static void main(String[] args) {
    var data = new int[] { 2, 4, 6 };
    var p = new ParamRefArray();
    System.out.println(p.update(data)[0]); //10
    System.out.println(data[0]); //2
  }
}
```

##null対策

```java
import java.util.Map;

public class BookMap {
  //ISBNコード：書名　で書籍情報を管理
  private Map<String, String> data;
  //引数mapで書籍情報を初期化
  public BookMap(Map<String, String> map) {
    this.data = map;
  }
  //ISBNコード（引数isbn）をキーに書名取得
  public String getTitleByIsbn(String isbn) {
    //指定されたキーがない場合nullを返す
    return this.data.get(isbn);
  }
}
```

```java:Main.java
import java.util.Map;
public class Main {
  public static void main(String[] args) {
    var b = new BookMap(Map.of(
        "978-4-7981-5757-3", "賢者の石",
        "978-4-7981-5202-8", "秘密の部屋",
        "978-4-7981-5382-7", "アズカバンの囚人"
        ));

    var title = b.getTitleByIsbn("978-4-7981-5757-3");
    if (title == null) {
      System.out.println("Disapparition!(ないです)");
    } else {
      System.out.println(title.trim()); //賢者の石
    }
  }
}
```

### Optionalクラスで上のコードを簡略化!
* null値の可能性のある値をOptinalでラッピング
* **Optional.ofNullableメソッド**
    * 与えられた値を元に`Optional<String>`（nullであるかもしれないString型の値）を生成しなさい！
    * nullであるかもしれないString型の値
* **optTitle.orElseメソッド**でOptionalオブジェクトが非null値を持つ場合その値を、それ以外で引数の値を返す
* →**null安全**（null処理強制）

```java
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;


public class BookMap {
  private Map<String, String> data = new HashMap<>();

  public BookMap(Map<String, String> map) {
    this.data = map;
  }

  public Optional<String> getTitleByIsbn(String isbn) {
    return Optional.ofNullable(this.data.get(isbn));
  }
}
```
```java
import java.util.HashMap;

public class OptionalNullCheck {

  public static void main(String[] args) {
    var b = new BookMap(
      new HashMap<String, String>() {
        {
          put("978-4-7981-5757-3", "賢者の石");
          put("978-4-7981-5202-8", "秘密の部屋");
          put("978-4-7981-5382-7", "アズカバンの囚人");
        }
      });

    var optTitle = b.getTitleByIsbn("978-4-7981-5382-7");
    var title = optTitle.orElse("×");
    System.out.println(title.trim()); //アズカバンの囚人
  }
}
```

* その他Optionalクラスのメンバー
* `isPresent` / `ifPresent`：nullチェック
* **`orElse` / `orElseGet`：nullチェックして値も取得する**

```java
import java.util.Optional;

public class OptionalExample {

  public static void main(String[] args) {
    //Optoinalオブジェクト生成
    var opt1 = Optional.of("サンプル1");
    //nullを許容
    var opt2 = Optional.ofNullable(null);
    var opt3 = Optional.empty();

    //値存在確認
    System.out.println(opt1.isPresent()); //true
    //値存在する場合にラムダ式実行
    opt1.ifPresent(value -> {
      System.out.println(value); //サンプル1
    });

    //opt2の値が存在する場合はその値、nullの場合引数を表示
    System.out.println(opt2.orElse("null値です")); //null値です

    //opt3がnullの場合ラムダ式実行
    System.out.println(opt3.orElseGet(() -> {
      return "null値です";
    })); //null値です
  }
}
```