【メモ】関数とラムダ式について理解する

# 関数オブジェクトとは
第1級オブジェクトとして扱う関数を関数オブジェクトという。

## 第1級オブジェクトとは
プログラムの実行中に生み出したり、変数に代入したりできるもの。(データ、データ構造、インスタンス、関数)
(補足)関数オブジェクトは参照型のためメモリ領域の先頭番地が変数に格納される。

### 第1級オブジェクトの特性
- 変数に代入できる。
- プログラム実行中に実体を生成できる。
- 引数として渡せる

## 関数オブジェクトの格納方法
```
// 静的メソッドの場合
関数型インターフェース 変数名 = クラス名::そのクラスの静的メソッド名

// インスタンスメソッドの場合
関数型インターフェース 変数名 = インスタンス変数名::そのインスタンスのメソッド名
```

## 標準関数インターフェース:java.util.functionパッケージ
関数オブジェクトを格納するための汎用的なインターフェースが準備されている。
引数が3つ以上の関数等用意されていない方も存在するため、そういった場合には自作が必要。
(補足)関数型インターフェースは***1つの抽象メソッドのみを持つ***という特徴を持つ。

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/function/package-summary.html

### 関数型インターフェースはAPIリファレンスに明記されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/612520/9da714f6-9882-1688-73b7-b572ccf83f57.png)


### 標準関数インターフェースの使用方法
参考：java.util.function以下の関数インターフェース使い方メモ

https://qiita.com/opengl-8080/items/22c4405a38127ed86a31

## 例1: 自作の関数インターフェースの使用例
```java:Func1.java
// 関数インターフェース
// アノテーションをつけることで明示的に関数インターフェースであることを宣言でき、
// 記法にミスがあったらコンパイラが警告を出してくれる。
@FunctionalInterface
public interface Func1 {
    boolean call(int x);    // インターフェースはabstract句が自動で付与される。
}
```

```java:FuncList.java
public class FuncList {
    public static boolean isOdd(int x) { return (x % 2 == 1); }    // 静的メソッド
}
```

```java:Main.java
    // 関数を格納
    Func1 func1 = FuncList::isOdd;

    // 抽象メソッド名で呼び出せる
    System.out.println(f1.call(1));
```

# ラムダ式とは
- 関数型インターフェイスを実装したクラスのインスタンスを簡単に作るための文法
- やりたい処理だけを手軽に短く書ける
- 実態は匿名クラスのため一つのメソッドのみから構成されるためイメージとしては匿名メソッドが近い?
    - インナークラスの特徴と同様にローカル変数には再代入することができず、暗黙的にfinalとして扱われる。
        例えば以下のコードをコンパイルするとエラーが発生する。
```java:ErrorLambda.java
        int num = 2;
        IntFunction<Integer> f1 = x -> { 
            num = 3;
            return x + num;
        };
```
:::note alert

"message": "Local variable num defined in an enclosing scope must be final or effectively final"
(訳)外側のスコープで定義されたローカル変数 num は、final または実質的に final でなければなりません
:::
- ラムダ式の構文を用いて関数型インターフェースで宣言されている***引数***と***戻り値を返す処理内容***を定義することでクラスとインスタンスを自動で生成してくれる。

## 例2: 例1をラムダ式で記述する場合の例
```java:Func1.java
// 同じ
@FunctionalInterface
public interface Func1 {
    boolean call(int x);
}
```

```java:FuncList.java
// 不要
```

```java:Main.java
    // ラムダ式で関数を定義
    Func1 func1 = x -> x % 2 == 1;

    // 抽象メソッド名で呼び出せる
    System.out.println(f1.call(1));
```

# StreamAPIとは
- コレクションの要素に対する操作が可能なAPI
- コレクションや配列から生成されたストリームに対して、メソッドを呼ぶことでデータ処理が可能
- 各要素に対して連鎖的に呼び出すことができる中間処理メソッド、最終的な戻り値を返す終端処理メソッドに分けられる。

## ストリームの生成方法
```
// コレクションの場合
Stream<T> st = コレクション<T>.stream();

// 配列の場合
Stream<T> st = Arrays.stream(T型の配列);
```

## 例3:Stream APiで指定した条件の要素のみ格納する。(コレクションの場合)
```java:処理
// フルーツの名前と糖度のフィールドが存在するフルーツインスタンスを格納したコレクションが存在するとする。
List<Fluits> fruitsList = new ArrayList<>();
fruitsList.add(apple);
fruitsList.add(banana);
fruitsList.add(orange);

// 各フルーツのインスタンスから糖度が15以上のフルーツ名をコレクションに格納する。
List<String> sweetfruitsList = fruitsList.stream()    // 要素をストリームとして生成する
                               .filter(fluits -> fluits.sugarLevelname <= 15)    // 糖度が15以上の要素を抽出
                               .map(fluits -> "甘い" + fluits.name)  // String型のフルーツ名フィールドをストリームに返す
                               .collect(Collectors.toList()};    // リストとして要素に格納する
```

# 参考文献
スッキリわかるJava入門 実践編 第3版