0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Lambda式メモ

Last updated at Posted at 2025-04-26

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式のシンプル化

  1. 引数型なしであれば、すべての引数の型の略が可能
  2. 一つの引数リストであれば、型やかっこがなくなてもよい
  3. 一行のメソッドのみ、スクエアブラケットの略も可能
  4. メソッドでは一行の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 IFSupplier 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);
	}
}
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?