Lambda式
導入
Java8のラムダ式は新たな特性として、コーディング効率にメリットがあるため、勉強する必要になると思います。そのため、本文はラムダ式に関する主な知識をまとめることになる。
jdk1.8
の新特性のひとつ、大部分の匿名内部クラスに代わりことで、コレクションのトラバース や他のコレクションの操作に便利さをもたらす。
以前、List
に対して匿名内部クラスで実現する場合、コードが複雑になる。。。
class LambdaDemo {
public static void main(String[] args) {
// 方式1: 匿名内部クラスで実現
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);
// 方式1: lambdaで実現
Collections.sort(list, (o1, o2) -> o1 - o2);
System.out.println("lambdaでsort後:" + list);
}
}
Lambda
式の型は関数ですが、Lambda
式はオブジェクト。特殊なオブジェクト型に依存する必要があり、それは、関数式IF。
シンプルに言うと、jdk1.8
の中でLambda
式は関数式IFのインスタンス。これがLambda
式と関数式IFとの関係。あるオブジェクトが関数式IFのインスタンスであれば、この対象はLambda
式で表示できる。
関数式インターフェースへの理解
Lambda
式利用には、重要な根拠の一つは、関数式IFにある。関数式IFとは、IFにて一つの抽象メソッドのみ。つまり、インスタンスにて抽象メソッドのみがある場合、このインスタンスは関数式インスタンス。
インスタンスに対し、@FunctionalInterface
が付けられている場合、コンパイラーが関数式IFの定義でIFを約束するようになる。インスタンスにて一つの抽象メソッドの定義が必要になる。抽象メソッドの複数定義や未定義となった場合、コンパイルエラーとなる。
@FunctionalInterface
public interface Flyable {
void showFly();
default void show() {
System.out.println("jdk1.8以降、IFにてデフォルトや静的メソッドの定義ができる");
}
}
// @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式の基本文法
本質的には匿名関数。戻り値型、メソッド名、引数リスト、メソッド本体により、構成される。
class lambdaDemo {
public static void main(String[] args) {
// 方式1: 匿名内部クラスで実現
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
シナリオ1:No Parameter No Return
public class NoParameterNoReturnDemo {
public static void main(String[] args) {
// 方法1: 匿名内部クラス
NoParameterNoReturn obj1 = new NoParameterNoReturn() {
@Override
public void test() {
System.out.println("No Parameter No return");
}
};
obj1.test();
// 方法2: Lambdaにより、実現
NoParameterNoReturn obj2 = () -> {
System.out.println("No Parameter No return");
};
}
}
public interface NoParameterNoReturn {
void test();
}
シナリオ2:No Return One Parameter
public class Test01 {
public static void main(String[] args) {
// 方法1
OneParameterNoreturn obj1 = new OneParameterNoreturn() {
@Override
public void test(int num) {
System.out.println("No return One parameter. : " + num);
}
};
obj1.test(10);
// 方法2
OneParameterNoreturn obj2 = (int num) -> {
System.out.println("No return One parameter. : " + num);
};
obj2.test(20);
}
}
public interface OneParameterNoreturn {
void test(int num);
}
シナリオ3:More Paramters No Return
public class Test01 {
public static void main(String[] args) {
// 方法1
MoreParameterNoreturn obj1 = new MoreParameterNoreturn() {
@Override
public void test(String str1, String str2) {
System.out.println(str1 + " : " + str2);
}
};
obj1.test("hello", "world");
// 方法2
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
シナリオ1
public class Test01 {
public static void main(String[] args) {
// 方法1
NoParameterHasReturn obj1 = new NoParameterHasReturn() {
@Override
public int test() {
// TODO 自動生成されたメソッド・スタブ
return 530;
}
};
System.out.println(obj1.test());
// 方法2
NoParameterHasReturn obj2 = () -> {return 530};
System.out.println(obj2.test());
}
}
public interface NoParameterHasReturn {
int test();
}
シナリオ2
public class Test01 {
public static void main(String[] args) {
// 方法1
OneParameterHasReturn obj1 = new OneParameterHasReturn() {
@Override
public String test(double num) {
// TODO 自動生成されたメソッド・スタブ
return "代入小数: " + num;
}
};
System.out.println(obj1.test(11.22));
// 方法2
OneParameterHasReturn obj2 = (double num) -> {
return "代入小数: " + num;
};
System.out.println(obj2.test(22.33));
}
}
public interface OneParameterHasReturn {
String test(double num);
}
シナリオ3
public class Test01 {
public static void main(String[] args) {
// 方法1
MoreParameterHasReturn obj1 = new MoreParameterHasReturn() {
@Override
public String test(int num1, int num2) {
return "計算結果: " + (num1 + num2);
}
};
System.out.println(obj1.test(1, 2));
// 方法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
式のシンプル化
- 引数型なしであれば、すべての引数の型の略が可能
- 一つの引数リストであれば、型やかっこがなくなてもよい
- 一行のメソッドのみ、スクエアブラケットの略も可能
- メソッドでは一行の
return
のみの場合、スクエアブラケットがなくてもよい。return
をなくす
public class Test01 {
public static void main(String[] args) {
// 1.引数型なしであれば、すべての引数の型の略が可能
// 既存
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");
// 2.一つの引数リストであれば、型やかっこがなくなてもよい。
// 既存
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));
// 3.一行のメソッドのみ、スクエアブラケットの略も可能
// Lambda式
NoParameterNoReturn obj5 = () -> {
System.out.println("NoParameterNoReturn");
};
obj5.test();
// Lambda式
NoParameterNoReturn obj6 = () -> System.out.println("NoParameterNoReturn");
obj6.test();
// 4.メソッドでは一行の`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では、五つのメソッド参照がある。
インスタンスメソッド参照、静的メソッド参照、特殊メソッド参照、コンストラクタメソッド参照、配列参照
public class Test01 {
public static void main(String[] args) {
// 方法1
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));
// 方法2 lambda式
Function<Double, Long> function2 = aDouble -> Math.round(aDouble);
System.out.println(function2.apply(3.14));
// 方法3 メソッド参照で実現
Function<Double, Long> function3 = Math::round;
System.out.println(function3.apply(3.15));
}
}
インスタンスメソッド参照
基本文法:
// Lambda式にて、オブジェクトを通じて、インスタンスメソッドを呼び出し
対象::メソッド()
前提条件:関数式IFの中で抽象メソッドの戻り値や引数リスト と
内部で対象を通じて、あるインスタンスメソッドの戻り値型や引数リスト が一致。
Consumer IF
とSupplier IF
を例にして、
public class Test01 {
public static void main(String[] args) {
/*
* 【例】ConsumerIFインスタンス、Overrideのaccept()で出力
* */
// 方式1
Consumer<String> consumer1 = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
consumer1.accept("hello world...");
// 方式2: lambdaで実現
Consumer<String> consumer2 = (str) -> System.out.println(str);
consumer2.accept("lambdaでプリント...");
// 方式3: メソッド参照で実現
Consumer<String> consumer3 = System.out::println;
consumer3.accept("メソッド参照");
/*
* 【例】supplierIFのインスタンス、Overrideのget()でReturn
* */
System.out.println("========== Supplier ==========");
Teacher teacher = new Teacher("jason", 18);
// 方式1
Supplier<String> supplier1 = new Supplier<String>() {
@Override
public String get() {
return teacher.getName();
}
};
System.out.println(supplier1.get());
// 方式2: lambda
Supplier<String> supplier2 = () -> teacher.getName();
System.out.println("lambda: " + supplier2.get());
// 方式3: メソッド参照
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 + "]";
}
}
静的メソッド参照
基本文法:
// Lambda式にて、オブジェクトを通じて、インスタンスメソッドを呼び出す
対象::メソッド()
前提条件:関数式IFの中で抽象メソッドの戻り値や引数リスト と
内部で対象を通じて、ある静的メソッドの戻り値型や引数リスト が一致。
// 方式1:匿名内部クラス
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));
// 方式2:lambda
Function<Double, Long> function2 = aDouble -> Math.round(aDouble);
System.out.println(function2.apply(3.14));
// 方式3:メソッド参照
Function<Double, Long> function3 = Math :: round;
System.out.println(function3.apply(3.14));
特殊メソッド参照
基本文法:
// Lambda式にて、メソッドの一番目の引数を通じて、指定したあるインスタンスメソッドを呼び出す
クラス::メソッド()
前提条件:関数式IFの中で抽象メソッドの一番目の引数をメソッドの呼び出す元にして、 二番目の引数(あるいは引数なし)が
呼び出す先の引数リストと対応できることで、かつ戻り値型と一致。
Comparator
を通じて、少数の大きさを比較
public class Test02 {
public static void main(String[] args) {
// 方式1
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));
// 方式2 lambdaで
Comparator<Double> comparator2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator2.compare(10.0, 20.0));
// 方法3 メソッド参照で実現
Comparator<Double> comparator3 = Double::compareTo;
System.out.println(comparator3.compare(10.0, 20.0));
}
}
コンストラクタメソッド参照
基本文法:
// Lambda式にて、指定したクラス名のインスタンスを返す
クラス名::new
前提条件:オブジェクトに関する引数リスト と 関数式IFにあるメソッドの引数リスト が一致、かつメソッドの戻り値型と生成オブジェクトの型が一致
SupplierIFのインスタンス対象、OverrideでTeacherを返す
public class Test02 {
public static void main(String[] args) {
/*
* SupplierIFのインスタンス対象、OverrideでTeacherを返す
* */
// 方式1: 匿名内部クラス
Supplier<Teacher> supplier1 = new Supplier<Teacher>() {
@Override
public Teacher get() {
return new Teacher();
}
};
System.out.println(supplier1.get());
// 方式2:Lambda
Supplier<Teacher> supplier2 = () -> new Teacher();
System.out.println(supplier2.get());
// 方式3:メソッド参照
Supplier<Teacher> supplier3 = Teacher::new;
System.out.println(supplier3.get());
}
}
FunctionIFのインスタンス対象、OverrideでTeacherを返す
public class Test02 {
public static void main(String[] args) {
/*
* FunctionIFのインスタンス対象、OverrideでTeacherを返す
* */
// 方式1: 匿名内部クラス
Function<String, Teacher> function1 = new Function<String, Teacher>() {
@Override
public Teacher apply(String name) {
// TODO 自動生成されたメソッド・スタブ
return new Teacher(name);
}
};
// 方式2:Lambda
Function<String, Teacher> function2 = (name) -> new Teacher(name);
System.out.println(function2.apply("jason"));
// 方式3:メソッド参照
Function<String, Teacher> function3 = Teacher::new;
System.out.println(function3.apply("jason"));
}
}
配列参照
FunctionIFのインスタンス対象、Overrideで指定長さのint型配列を返す
public class Test02 {
public static void main(String[] args) {
/*
* FunctionIFのインスタンス対象、Overrideで指定長さのint型配列を返す
* */
// 方式1:匿名内部クラス
Function<Integer, int[]> function1 = new Function<Integer, int[]>() {
@Override
public int[] apply(Integer integer) {
// TODO 自動生成されたメソッド・スタブ
return new int[integer];
}
};
// 方式2:Lambda
Function<Integer, int[]> function2 = (num) -> new int[num];
System.out.println(function2.apply(20));
// 方式3:メソッド参照
Function<Integer, int[]> function3 = int[]::new;
System.out.println(Arrays.toString(function3.apply(30)));
}
}
Lambda:コレクションでの利用
forEach()
コレクションにて、forEach()メソッドの引数はConsumer
になり、lambda
でListやsetをトラバース。
listにある要素をトラバース
public class Test02 {
public static void main(String[] args) {
/*
* FunctionIFのインスタンス対象、Overrideで指定長さのint型配列を返す
* */
List<Integer> list = Arrays.asList(11,22,33,44,55)
// 方式1:匿名内部クラス
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer element) {
System.out.println(element);
}
});
// 方式2:Lambda
list.forEach(element -> System.out.println(element));
// 方式3:メソッド参照
list.forEach(System.out::println);
}
}
setにある要素をトラバース
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);
// 方式2:Lambda
list.forEach(element -> System.out.println(element));
// 方式3:メソッド参照
list.forEach(System.out::println);
}
}
MapにてforEachメソッドの引数はBiConsumerIF。
BiConsumerIFは2引数があるConsumerIFに属する。
このメソッドとLambdaとの組み合わせでMapにある要素をトラバース
public class Test02 {
public static void main(String[] args) {
/*
* MapにてforEachメソッドの引数はBiConsumerIF。
* BiConsumerIFは2引数があるConsumerIFに属する。
* このメソッドとLambdaとの組み合わせでMapにある要素をトラバース
* */
HashMap<String, String> map = new HashMap<>();
map.put("jason", "USA");
map.put("musk", "UK");
map.put("hanny", "KR");
// 方式1
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println("key: " + key + "value: " + value);
}
});
// 方式2 Lambda
map.forEach((key, value) -> System.out.println("key: " + key + "value: " + value));
}
}
removeIf()メソッド
コレクションにて、removeIf()メソッドの引数はPredicateIF、このメソッドとLambdaで、Listやsetにある要素をトラバース
Listにある要素を削除
public class Test03 {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("aa", "bb", "cc", "dd"));
// 方式1 匿名内部クラスで実現
list.removeIf(new Predicate<String>() {
@Override
public boolean test(String element) {
return "bb".equals(element);
}
});
System.out.println(list);
//方式2: Lambda
list.removeIf("bb"::equals);
}
}
setにある要素を削除
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);
// 方式1 匿名内部クラスで実現
hashSet.removeIf(new Predicate<String>() {
@Override
public boolean test(String element) {
return "bb".equals(element);
}
});
System.out.println(hashSet);
//方式2:Lambda
hashSet.removeIf("bb"::equals);
System.out.println(hashSet);
}
}