Lambda式メモ

# Lambda式
#### 導入
Java8のラムダ式は新たな特性として、コーディング効率にメリットがあるため、勉強する必要になると思います。そのため、本文はラムダ式に関する主な知識をまとめることになる。
`jdk1.8`の新特性のひとつ、大部分の匿名内部クラスに代わりことで、コレクションのトラバース や他のコレクションの操作に便利さをもたらす。
以前、`List`に対して匿名内部クラスで実現する場合、コードが複雑になる。。。
```java
class LambdaDemo {
	public static void main(String[] args) {
		// 方式１：　匿名内部クラスで実現
		List<Integer> list = Arrays.asList(3,6,1,7,2,5,4);
		Collections.sort(list, new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				return o1 - o2;
			}
		});
		System.out.println("sort後：　"+ list);
  
		// 方式１： lambdaで実現
		Collections.sort(list, (o1, o2) -> o1 - o2);
		System.out.println("lambdaでsort後：" + list);
		
	}
}
```

`Lambda`式の型は関数ですが、`Lambda`式はオブジェクト。特殊なオブジェクト型に依存する必要があり、それは、関数式ＩＦ。

シンプルに言うと、`jdk1.8`の中で`Lambda`式は関数式ＩＦのインスタンス。これが`Lambda`式と関数式ＩＦとの関係。あるオブジェクトが関数式ＩＦのインスタンスであれば、この対象は`Lambda`式で表示できる。

#### 関数式インターフェースへの理解
`Lambda`式利用には、重要な根拠の一つは、関数式ＩＦにある。関数式ＩＦとは、IFにて一つの抽象メソッドのみ。つまり、インスタンスにて抽象メソッドのみがある場合、このインスタンスは関数式インスタンス。

インスタンスに対し、`@FunctionalInterface`が付けられている場合、コンパイラーが関数式IFの定義でIFを約束するようになる。インスタンスにて一つの抽象メソッドの定義が必要になる。抽象メソッドの複数定義や未定義となった場合、コンパイルエラーとなる。
```java
@FunctionalInterface
public interface Flyable {
	void showFly();
	default void show() {
		System.out.println("jdk1.8以降、IFにてデフォルトや静的メソッドの定義ができる");
	}
}
```

```java
// @FunctionalInterfaceでが明示されない
public interface Flyable {
    void showFly();
}
public class FunctionnalInterface {
	public static void main(String[] args) {
		Flyable flyable = () -> {
			System.out.println("fly...");
		};
		// call back
		flyable.showFly();		
	}

}
```

#### Lambdaと匿名内部クラス
- 型の違う
  - 匿名内部クラス：IF、抽象クラス、インスタンス
  - lambda式：IFのみ
- 利用制限
  - IFにて一つの抽象メソッドのみの場合、両者が利用可能
  - 複数の抽象メソッドがある場合、匿名内部クラスのみが可能
- 実現原理の違い
  - 匿名内部クラス：コンパイル後、独自の`.class`ファイルが生成された
  - `Lambda式`：コンパイル後、独自の`.class`ファイル生成なし

#### Lambda式の基本文法
本質的には匿名関数。戻り値型、メソッド名、引数リスト、メソッド本体により、構成される。
```JAVA
class lambdaDemo {
	public static void main(String[] args) {
		// 方式１：　匿名内部クラスで実現
		List<Integer> list = Arrays.asList(3,6,1,7,2,5,4);
		Collections.sort(list, new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				return o1 - o2;
			}
		});
		System.out.println("sort後：　"+ list);
		
		// 方式2： lambdaで実現
		Collections.sort(list, (o1, o2) -> o1 - o2);
		System.out.println("lambdaでsort後：" + list);

	}
```

#### Lambda式の基本利用

##### 戻り値なし関数式IF

シナリオ１：No Parameter No Return
```java
public class NoParameterNoReturnDemo {
	public static void main(String[] args) {
		// 方法１：　匿名内部クラス
		NoParameterNoReturn obj1 = new NoParameterNoReturn() {
			@Override
			public void test() {
				System.out.println("No Parameter No return");
			}
		};
		obj1.test();
		
		//　方法２：　Lambdaにより、実現
		NoParameterNoReturn obj2 = () -> {
			System.out.println("No Parameter No return");
		};	
	}
}

public interface NoParameterNoReturn {
	void test();
}
```

シナリオ２：No Return One Parameter
```java
public class Test01 {
	public static void main(String[] args) {
		// 方法１
		OneParameterNoreturn obj1 = new OneParameterNoreturn() {
			@Override
			public void test(int num) {
				System.out.println("No return One parameter. : " + num);
			}
		};
		obj1.test(10);
		
		//　方法２
		OneParameterNoreturn obj2 = (int num) -> {
			System.out.println("No return One parameter. : " + num);
		};
		obj2.test(20);
	}
}

public interface OneParameterNoreturn {
	void test(int num);
}
```

シナリオ３：More Paramters No Return
```java
public class Test01 {
	public static void main(String[] args) {
		// 方法１
		MoreParameterNoreturn obj1 = new MoreParameterNoreturn() {
			@Override
			public void test(String str1, String str2) {
				System.out.println(str1 + " : " + str2);	
			}
		};
		obj1.test("hello", "world");
		
		//　方法２
		MoreParameterNoreturn obj2 = (String str1, String str2) -> {
			System.out.println(str1 + " : " + str2);	
		};
		obj2.test("hello", "java");
	}
}

public interface MoreParameterNoreturn {
	void test(String str1, String str2);
}
```

##### 戻り値なしの関数式IF
シナリオ１
```java
public class Test01 {
	public static void main(String[] args) {
		// 方法１
		NoParameterHasReturn obj1 = new NoParameterHasReturn() {
			
			@Override
			public int test() {
				// TODO 自動生成されたメソッド・スタブ
				return 530;
			}
		};
		System.out.println(obj1.test());
		
		//　方法２
		NoParameterHasReturn obj2 = () -> {return 530};
		System.out.println(obj2.test());
	}
}

public interface NoParameterHasReturn {
	int test();
}
```

シナリオ２
```java
public class Test01 {
	public static void main(String[] args) {
		// 方法１
		OneParameterHasReturn obj1 = new OneParameterHasReturn() {
			@Override
			public String test(double num) {
				// TODO 自動生成されたメソッド・スタブ
				return "代入小数：　" + num;
			}
		};
		System.out.println(obj1.test(11.22));
		
		//　方法２
		OneParameterHasReturn obj2 = (double num) -> {
			return "代入小数：　" + num;
		};
		System.out.println(obj2.test(22.33));
	}
}

public interface OneParameterHasReturn {
	String test(double num);
}
```
シナリオ３
```java
public class Test01 {
	public static void main(String[] args) {
		// 方法１
		MoreParameterHasReturn obj1 = new MoreParameterHasReturn() {
			@Override
			public String test(int num1, int num2) {
				return "計算結果：　" + (num1 + num2);
			}
		};
		System.out.println(obj1.test(1, 2));
		
		//　方法２
		MoreParameterHasReturn obj2 = (int num1, int num2) -> {
			return "計算結果：　" + (num1 + num2);
		};
		System.out.println(obj2.test(3, 5));
	}
}

public interface MoreParameterHasReturn {
	String test(int num1, int num2);
}
```
#### `Lambda`式のシンプル化

1. 引数型なしであれば、すべての引数の型の略が可能
2. 一つの引数リストであれば、型やかっこがなくなてもよい
3. 一行のメソッドのみ、スクエアブラケットの略も可能
4. メソッドでは一行の`return`のみの場合、スクエアブラケットがなくてもよい。`return`をなくす
```java
public class Test01 {
    public static void main(String[] args) {
        // １．引数型なしであれば、すべての引数の型の略が可能
        // 既存
        MoreParameterNoReturn obj1 = (String str1, String str2) -> {
            System.out.println(str1 + " : " + str2);
        };
        obj1.test("hello", "world");
        // Lambdaの利用後
        MoreParameterNoReturn obj2 = (str1, str2) -> {
            System.out.println(str1 + " : " + str2);
        };
        obj2.test("hello", "world");

        // ２．一つの引数リストであれば、型やかっこがなくなてもよい。
        // 既存
        OneParameterHasReturn obj3 = (double num) -> {
            return "小数：" + num;
        };
        System.out.println(obj3.test(520.0));
        // Lambdaの利用後
        OneParameterHasReturn obj4 = num -> {
            return "小数：" + num;
        };
        System.out.println(obj4.test(1314.0));

        // ３．一行のメソッドのみ、スクエアブラケットの略も可能
        // Lambda式
        NoParameterNoReturn obj5 = () -> {
            System.out.println("NoParameterNoReturn");
        };
        obj5.test();
        // Lambda式
        NoParameterNoReturn obj6 = () -> System.out.println("NoParameterNoReturn");
        obj6.test();

        // ４．メソッドでは一行の`return`のみの場合、スクエアブラケットがなくてもよい。`return`をなくす。
        // 既存
        MoreParameterHasReturn obj7 = (int a, int b) -> {
            return "结果：" + (a + b);
        };
        System.out.println(obj7.test(10, 20));
        // Lambda式
        MoreParameterHasReturn obj8 = (a, b) -> "结果：" + (a + b);
        System.out.println(obj8.test(20, 30));
    }
}
```

#### メソッド参照
Lambdaでは、五つのメソッド参照がある。
インスタンスメソッド参照、静的メソッド参照、特殊メソッド参照、コンストラクタメソッド参照、配列参照
```java
public class Test01 {
	public static void main(String[] args) {
		// 方法１
		Function<Double, Long> function1 = new Function<Double, Long>() {
			@Override
			public Long apply(Double aDouble) {
				return Math.round(aDouble);
			}
		};
		System.out.println(function1.apply(2323.22));
		
		// 方法２ lambda式
		Function<Double, Long> function2 = aDouble -> Math.round(aDouble);
		System.out.println(function2.apply(3.14));
		
		// 方法３ メソッド参照で実現
		Function<Double, Long> function3 = Math::round;
		System.out.println(function3.apply(3.15));
	}
}
```

##### インスタンスメソッド参照
基本文法：
```java
// Lambda式にて、オブジェクトを通じて、インスタンスメソッドを呼び出し
対象::メソッド()
```
前提条件：関数式IFの中で抽象メソッドの戻り値や引数リスト　と　

内部で対象を通じて、あるインスタンスメソッドの戻り値型や引数リスト　が一致。

`Consumer IF`と`Supplier IF`を例にして、

```java
public class Test01 {
	public static void main(String[] args) {
		/*
		 * 【例】ConsumerIFインスタンス、Overrideのaccept()で出力
		 * */
		//　方式１
		Consumer<String> consumer1 = new Consumer<String>() {
			@Override
			public void accept(String str) {
				System.out.println(str);
			}
		};
		consumer1.accept("hello world...");
		
		// 方式２：　lambdaで実現
		Consumer<String> consumer2 = (str) -> System.out.println(str);
		consumer2.accept("lambdaでプリント...");
		
		// 方式３: メソッド参照で実現
		Consumer<String> consumer3 = System.out::println;
		consumer3.accept("メソッド参照");
		
		/*
		 * 【例】supplierIFのインスタンス、Overrideのget()でReturn
		 * */
		System.out.println("========== Supplier ==========");
		Teacher teacher = new Teacher("jason", 18);
		//　方式１
		Supplier<String> supplier1 = new Supplier<String>() {
			@Override
			public String get() {
				return teacher.getName();
			}
		};
		System.out.println(supplier1.get());

		// 方式２: lambda
		Supplier<String> supplier2 = () -> teacher.getName();
		System.out.println("lambda: " + supplier2.get());
		
		// 方式３：　メソッド参照
		Supplier<String> supplier3 = teacher::getName;
		System.out.println("メソッド参照：　" + supplier3.get());
	}
}

class Teacher {
	String name;
	int age;
	
	public Teacher(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Teacher [name=" + name + ", age=" + age + "]";
	}
}
```
##### 静的メソッド参照
基本文法：
```java
// Lambda式にて、オブジェクトを通じて、インスタンスメソッドを呼び出す
対象::メソッド()
```
前提条件：関数式IFの中で抽象メソッドの戻り値や引数リスト　と　
内部で対象を通じて、ある静的メソッドの戻り値型や引数リスト　が一致。
```java
//　方式１：匿名内部クラス
Function<Double, Long> function1 = new Function<Double, Long>() {
    @Override
    public Long apply(Double aDouble) {
        return Math.round(aDouble);
    }
};
System.out.println(function1.apply(3.14));

// 方式２：lambda
Function<Double, Long> function2 = aDouble -> Math.round(aDouble);
System.out.println(function2.apply(3.14));

// 方式３：メソッド参照
Function<Double, Long> function3 = Math :: round;
System.out.println(function3.apply(3.14));
```
##### 特殊メソッド参照
基本文法：
```java
// Lambda式にて、メソッドの一番目の引数を通じて、指定したあるインスタンスメソッドを呼び出す
クラス::メソッド()
```
前提条件：関数式IFの中で抽象メソッドの一番目の引数をメソッドの呼び出す元にして、　二番目の引数（あるいは引数なし）が　

呼び出す先の引数リストと対応できることで、かつ戻り値型と一致。

`Comparator`を通じて、少数の大きさを比較

```java
public class Test02 {
	public static void main(String[] args) {
		//　方式１
		Comparator<Double> comparator1 = new Comparator<Double>() {
			@Override
			public int compare(Double o1, Double o2) {
				return o1.compareTo(o2);
			}
		};
		System.out.println(comparator1.compare(10.0, 20.0));
        
		//　方式２ lambdaで
		Comparator<Double> comparator2 = (o1, o2) -> o1.compareTo(o2);
		System.out.println(comparator2.compare(10.0, 20.0));
		
		// 方法３　メソッド参照で実現
		Comparator<Double> comparator3 = Double::compareTo;
		System.out.println(comparator3.compare(10.0, 20.0));
	}
}
```
##### コンストラクタメソッド参照
基本文法：
```java
// Lambda式にて、指定したクラス名のインスタンスを返す
クラス名::new
```

前提条件：オブジェクトに関する引数リスト　と　関数式IFにあるメソッドの引数リスト　が一致、かつメソッドの戻り値型と生成オブジェクトの型が一致

>  SupplierIFのインスタンス対象、OverrideでTeacherを返す

```java
	public class Test02 {
		public static void main(String[] args) {
			/*
			 *　SupplierIFのインスタンス対象、OverrideでTeacherを返す
			 * */
			//　方式１：　匿名内部クラス
			 Supplier<Teacher> supplier1 = new Supplier<Teacher>() {
				
				@Override
				public Teacher get() {
					return new Teacher();
				}
			};
			System.out.println(supplier1.get());
			
			// 方式２：Lambda
			Supplier<Teacher> supplier2 = () -> new Teacher();
			System.out.println(supplier2.get());
			
			// 方式３：メソッド参照
			Supplier<Teacher> supplier3 = Teacher::new;
			System.out.println(supplier3.get());
		}
}
```
> FunctionIFのインスタンス対象、OverrideでTeacherを返す
```java
	public class Test02 {
		public static void main(String[] args) {
			/*
			 *　FunctionIFのインスタンス対象、OverrideでTeacherを返す
			 * */
			//　方式１：　匿名内部クラス
			Function<String, Teacher> function1 = new Function<String, Teacher>() {
				
				@Override
				public Teacher apply(String name) {
					// TODO 自動生成されたメソッド・スタブ
					return new Teacher(name);
				}
			};
			
			// 方式２：Lambda
			Function<String, Teacher> function2 = (name) -> new Teacher(name);
			System.out.println(function2.apply("jason"));
			
			// 方式３：メソッド参照
			Function<String, Teacher> function3 = Teacher::new;
			System.out.println(function3.apply("jason"));
		}
}
```

##### 配列参照

> FunctionIFのインスタンス対象、Overrideで指定長さのint型配列を返す

```java
	public class Test02 {
		public static void main(String[] args) {
			/*
			 *　FunctionIFのインスタンス対象、Overrideで指定長さのint型配列を返す
			 * */
			//　方式１：匿名内部クラス
			Function<Integer, int[]> function1 = new Function<Integer, int[]>() {
				
				@Override
				public int[] apply(Integer integer) {
					// TODO 自動生成されたメソッド・スタブ
					return new int[integer];
				}
			};
			// 方式２：Lambda
			Function<Integer, int[]> function2 = (num) -> new int[num];
			System.out.println(function2.apply(20));
			
			// 方式３：メソッド参照
			Function<Integer, int[]> function3 = int[]::new;
			System.out.println(Arrays.toString(function3.apply(30)));
		}
}

```
#### Lambda：コレクションでの利用
##### forEach()
コレクションにて、forEach()メソッドの引数は`Consumer`になり、`lambda`でListやsetをトラバース。
> listにある要素をトラバース
```java
	public class Test02 {
		public static void main(String[] args) {
			/*
			 *　FunctionIFのインスタンス対象、Overrideで指定長さのint型配列を返す
			 * */
			List<Integer> list = Arrays.asList(11,22,33,44,55)
			//　方式１：匿名内部クラス
			list.forEach(new Consumer<Integer>() {
				@Override
				public void accept(Integer element) {
					System.out.println(element);
				}
			});
			
			// 方式２：Lambda
			list.forEach(element -> System.out.println(element));
			
			// 方式３：メソッド参照
			list.forEach(System.out::println);

		}
}
```

> setにある要素をトラバース

```java
	public class Test02 {
		public static void main(String[] args) {
			/*
			 *　FunctionIFのインスタンス対象、Overrideで指定長さのint型配列を返す
			 * */
			List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
			HashSet<String> hashSet = new HashSet<>(list);
			
			// 方式２：Lambda
			list.forEach(element -> System.out.println(element));
			
			// 方式３：メソッド参照
			list.forEach(System.out::println);
		}
}

```

> MapにてforEachメソッドの引数はBiConsumerIF。
> BiConsumerIFは２引数があるConsumerIFに属する。
> このメソッドとLambdaとの組み合わせでMapにある要素をトラバース

```java
public class Test02 {
	public static void main(String[] args) {
		/*
		 *　MapにてforEachメソッドの引数はBiConsumerIF。
		 *　BiConsumerIFは２引数があるConsumerIFに属する。
		 *　このメソッドとLambdaとの組み合わせでMapにある要素をトラバース
		 * */
		HashMap<String, String> map = new HashMap<>();
		map.put("jason", "USA");
		map.put("musk", "UK");
		map.put("hanny", "KR");
  
		// 方式１
		map.forEach(new BiConsumer<String, String>() {
			@Override
			public void accept(String key, String value) {
				System.out.println("key: " + key +  "value: " + value);
			}
		});
		
		// 方式２　Lambda
		map.forEach((key, value) -> System.out.println("key: " + key +  "value: " + value));
	}
}
```
##### removeIf()メソッド
コレクションにて、removeIf()メソッドの引数はPredicateIF、このメソッドとLambdaで、Listやsetにある要素をトラバース
> Listにある要素を削除
```java
public class Test03 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>(Arrays.asList("aa", "bb", "cc", "dd"));
		// 方式１　匿名内部クラスで実現
		list.removeIf(new Predicate<String>() {
			@Override
			public boolean test(String element) {
				return "bb".equals(element);
			}
		
		});
		System.out.println(list);
		
		//方式２：　Lambda
		list.removeIf("bb"::equals);
	}
}
```
> setにある要素を削除
```java
public class Test03 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>(Arrays.asList("aa", "bb", "cc", "dd"));
		HashSet<String> hashSet = new HashSet<>(list);
  
		// 方式１　匿名内部クラスで実現
		hashSet.removeIf(new Predicate<String>() {
			@Override
			public boolean test(String element) {
				return "bb".equals(element);
			}
		});
		System.out.println(hashSet);

		//方式２：Lambda
		hashSet.removeIf("bb"::equals);
		System.out.println(hashSet);
	}
}
```