본문 바로가기

자바(JAVA)
[자바(Java)] 람다식

// 함수형 인터페이스 - 람다식 제공 원리

- 평범한 인터페이스

- 목적: 람다식을 저장하기 위해서 만들어진 인터페이스

1. 표준 API 함수형 인터페이스 > JDK 제공
2. 사용자 정의 함수형 인터페이스 > 개발자가 선언


package com.sorrel012.functionalprogramming;

import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;


//Stream<T> filter(Predicate<? super T> predicate);
//boolean test(T t);
class EvenNumberPredicate implements Predicate<Integer> {

	@Override
	public boolean test(Integer number) {
		return number%2 == 0;
	}
	
}

//void forEach(Consumer<? super T> action);
//void accept(T t);
class PrintlnConsumer implements Consumer<Integer> {

	@Override
	public void accept(Integer num) {
		System.out.println(num);
	}
	
}

//Stream<R> map(Function<? super T, ? extends R> mapper);
//R apply(T t);
class NumberSquareMapper implements Function<Integer, Integer> {

	@Override
	public Integer apply(Integer num) {
		return num*num;
	}
	
}

public class LambdaBehindTheScreensRunner {
	
	public static void main(String[] args) {

		List.of(23,43,34,45,36,48).stream()
								.filter(n -> n%2==0)
								.forEach(System.out::println);
		System.out.println();

		List.of(23,43,34,45,36,48).stream()
								.filter(new EvenNumberPredicate())
								.forEach(System.out::println);
		System.out.println();
		
		List.of(23,43,34,45,36,48).stream()
								.filter(new EvenNumberPredicate())
								.forEach(new PrintlnConsumer());
		System.out.println();
		
		List.of(23,43,34,45,36,48).stream()
								.filter(new EvenNumberPredicate())
								.map(n -> n*n)
								.forEach(new PrintlnConsumer());
		System.out.println();
		
		List.of(23,43,34,45,36,48).stream()
								.filter(new EvenNumberPredicate())
							    .map(new NumberSquareMapper())
								.forEach(new PrintlnConsumer());
	
	} //main

}

// 람다식(Lambda Expression)

- 함수(메서드)를 간단한 식(expression)으로 표현하는 방법

-함수형 프로그래밍 방식을 지원하는 표현식
- 람다식을 사용하면 코드 일부가 간결해진다.
- Java 8 출시
- 목적" 컬렉션(배열) 조작

- 자바의 메서드를 표현하는 방법 중 하나로 사용
- 메서드를 대신하는 코드블록
- 자바의 람다식은 인터페이스를 사용해서 만든다.
- 자바의 람다식은 익명 객체(추상 메서드)를 만드는 표현을 간소화시킨 기술이다.

- 익명 객체(익몀ㅇ 함수x) : 함수형 인터페이스 타입의 참조 변수가 필요!
    ~ 함수형 인터페이스의 메서드와 람다식의 매개변수 개수, 반환타입이 일치해야 함.

- 람다식 작성 규칙
    ~ 메서드의 이름과 반환 타입을 제거하고 '->'를 블록{} 앞에 추가
    ~ 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에 ; 안 붙임.)
    ~ 매개변수 타입이 추론 가능하면 생략 가능(대부분)
    ~ 매개변수가 하나인 경우, 소괄호() 생략 가능(타입이 없을 때만!)
    ~ 블록 안의 문장이 하나일 때, 중괄호{} 생략 가능(끝에 ; 안 붙임.) // return문인 경우 생략 불가

//메서드
int method(int i) {
	return (int)(math.random()*5) + 1;
}

//람다식
(i) -> (int)(Math.random()*5) + 1)

 

int max(int a, int b) {
	return a > b  ? a : b;
}

//람다식
(a, b) -? a > b ? a : b

 

int printVar(String name, int i) {
	System.out.println(name + "=" + i) ;
}

//람다식
(name, i) -> System.out.println(name + "=" + i)

 

int square(int x) {
	return x * x;
}

//람다식
(x) -> x * x

 


public class Ex01 {
	public static void main(String[] args) {
		MyFunction f = (a,b) -> a > b ? a : b;
		
		int value = f.max(3, 5);
		System.out.println("value = " + value);
	}
}

@FunctionalInterface
interface MyFunction {
	int max(int a, int b);
}

@FunctionalInterface
interface MyFunction2 {
	void run();
}

public class Ex02 {
	static void execute(MyFunction2 f) {
		f.run();
	}
	
	static MyFunction2 getMyFunction() { //함수형 인터페이스 직접 구현(람다식과 관련x)
		MyFunction2 f = () -> System.out.println("f3.run()");
		return f;
	}
	
	public static void main(String[] args) {
		MyFunction2 f1 = () -> System.out.println("f1.run()");
		
		MyFunction2 f2 = new MyFunction2() {
			public void run() {
				System.out.println("f2.run()");
			}
		};
		
		MyFunction2 f3 = getMyFunction();
		
		f1.run();
		f2.run();
		f3.run();
		
		execute(f1);
		execute ( () -> System.out.println("run()") );
		
	}
}

package com.test.lambda;

public class Ex87_lambda {
	
	public static void main(String[] args) {

		//추상 메서드의 형식에 따라
		// - 매개변수 O, X
		// - 반환값 O, X
		
		//1. 매개변수 X, 반환값 X	//void call();
		NoParameterNoReturn pr1 = new NoParameterNoReturn() {
			@Override
			public void call() {
				System.out.println("pr1");
			}
		};
		pr1.call();
		
		NoParameterNoReturn pr2 = () -> {System.out.println("pr2");};
		pr2.call();
		
		NoParameterNoReturn pr3 = () -> System.out.println("pr3"); //★★람다식 구현부에 있는 실행 문장이 한 줄이면 {}을 생략할 수 있다.
		pr3.call();
		
		System.out.println();
		
		//2-1. 매개변수 한 개 O, 반환값 X	//void call(int n);
		ParameterNoReturn pr4 = (int n) -> {System.out.println("pr4: " + n);};
		pr4.call(100);
		pr4.call(200);
		pr4.call(300);

		ParameterNoReturn pr5 = (n) -> {System.out.println("pr5: " + n);};
		pr5.call(400);
		
		ParameterNoReturn pr6 = n ->  {System.out.println("pr6: " + n);};
		pr6.call(500);
		
		ParameterNoReturn pr7 = n -> System.out.println("pr7: " + n);
		pr7.call(600);
		
		System.out.println();
		
		//2-2. 매개변수 여러 개 O, 반환값 X //void call(String name, int age);
		MultiParameterNoReturn pr8 = (String name, int age) -> {System.out.println("pr8: " + name + ", " + age);};
		pr8.call("홍길동", 20);
		
		MultiParameterNoReturn pr9 = (name, age) -> {System.out.println("pr9: " + name + ", " + age);};
		pr9.call("아무개", 25);
		
		System.out.println();
		
		//3. 매개변수 X, 반환값 O	//int call();
		NoParameterReturn pr11 = () -> {return 100;};
		System.out.println("pr11: " + pr11.call());
		

		NoParameterReturn pr12 = () -> 200;
		System.out.println("pr12: " + pr12.call());
		
		System.out.println();
		
		//4. 매개변수 O, 반환값 O	//int call(int a, int b);
		ParameterReturn pr13 = (int a, int b) -> {return a + b;};
		System.out.println("pr13: " + pr13.call(10, 20));

		ParameterReturn pr14 = (a, b) -> a + b;
		System.out.println("pr14: " + pr14.call(20, 30));
		
		
				
	}//main

} //Main

interface NoParameterNoReturn {
	void call();
}

interface ParameterNoReturn {
	void call(int n);
}

interface MultiParameterNoReturn {
	void call(String name, int age);
}

interface NoParameterReturn {
	int call();
}

interface ParameterReturn {
	int call(int a, int b);
}

// java.util.function 패키지

- 자주 사용되는 다양한 함수형 인터페이스를 제공

① java.lang.Runnable : 매개변수x, 반환값x

void run()

② Supplier<T> : 매개변수x, 반환값o

T get()

③ Consumer<T> : 매개변수o, 반환값x

void accept(T t)

④ Function<T,R> : 일반적인 함수. 매개변수1, 반환값 o

R apply(T t)

⑤ Predicate<T> : 조건식을 표현하는 데 사용됨. 매개변수2, 반환값 boolean

boolean test(T t)

 

⑥ BiConsumer<T,U> : 매개변수2, 반환값x

void accept(T t, U u)

⑦ BiPredicate<T,U> : 조건식을 표현하는 데 사용됨. 매개변수2, 반환값 boolean

boolean test(T t, U u)

⑧ BiFunction<T,U,R> : 매개변수2, 반환값 1

R apply(T t, U u)

⑨ UnaryOperator<T> : Function의 자손. 매개변수와 결과의 타입이 같음.

T apply(T t)

⑩ BinaryOperator<T> : BiFunction의 자손. 매개변수와 결과의 타입이 같음.

T apply(T t, T t)

import java.util.ArrayList;
import java.util.List;
import java.util.function.*;

public class Ex03 {
	public static void main(String[] args) {
		Supplier<Integer> s = () -> (int)(Math.random()*100)+1;
		Consumer<Integer> c = i -> System.out.print(i + ", ");
		Predicate<Integer> p = i -> i % 2 == 0;
		Function<Integer, Integer> f = i -> i/10*10; //i의 1의 자리 없애기
		
		List<Integer> list = new ArrayList<>();
		makeRandomList(s, list);
		System.out.println(list);
		printEvenNum(p, c, list);
		List<Integer> newList = doSomething(f, list);
		System.out.println(newList);
	}
	
	static <T> List<T> doSomething(Function<T, T> f, List<T> list) {
		List<T> newList = new ArrayList<T>(list.size());	
		for(T i : list) {
			newList.add(f.apply(i));
		}	
		return newList;
	}
	
	static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
		System.out.print("[");
		for(T i : list) {
			if(p.test(i)) {
				c.accept(i);
			}
		}
		System.out.println("]");
	}
	
	static <T> void makeRandomList(Supplier<T> s, List<T> list) {
		for(int i = 0; i < 10; i++) {
			list.add(s.get());
		}
	}
	
}

// Predicate의 결합

- and(), or(), negate() 로 두 Predicate를 하나로 결합(default메서드)

Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q= i -> i < 200;
Predicate<Integer> r = i -> i % 2 == 0;

//결합
Predicate<Integer> notP = p.negate(); // i >= 100
Predicate<Integer> all = notP.and(q).or(r); // 100 < ==i && i < 200 || i % 2 == 0
Predicate<Integer> all2 = notP.and(q.or(r)); // 100 < ==i && (i < 200 || i % 2 == 0)


- 등가비교를 위해 isEqual()을 사용(static 메서드)

Boolean result = Predicate.isEqual(str1).test(str2);

import java.util.function.Predicate;

public class Ex04 {

	public static void main(String[] args) {
		Predicate<Integer> p = i -> i < 100;
		Predicate<Integer> q = i -> i < 200;
		Predicate<Integer> r = i -> i%2 == 0;
		Predicate<Integer>  notP = p.negate(); // i >= 100
		
		Predicate<Integer> all = notP.and(q.or(r));
		System.out.println("all.test(150) = " + all.test(150));
		
		String str1 = "abc";
		String str2 = "abc";
		
		boolean result = Predicate.isEqual(str1).test(str2);
		System.out.println("isEqual() 결과 = " + result);
	}

}

// 컬렉션 프레임웍과 함수형 인터페이스

- 함수형 인터페이스를 사용하는 컬렉션 프레임웍 메서드(와일드카드 생략)

① Collection - 조건에 맞는 요소를 삭제

boolean removelf(Predicate<E> filter) 


② List - 모든 요소를 변환하여 대체

void replaceAll(UnaryOperator<E> operator)

③ Iterable - 모든 요소에 작업 action 수행

void forEach(Consumer<T> action)

④ Map - 지정된 키의 값에 작업 f 수행

V compute(K key, BiFunction<K,V,V> f)

 Map - 키가 없으면 작업 f 수행 후 추가

V computeifAbsent(K key, Function<K,V> f)

 Map - 지정된 키가 있을 때 작업 f 수행

V computeIfPresent(K key, BiFunction<K,V,V> f)

 Map - 모든 요소에 병합작업 f 수행

V merge(K key, V value, BiFunction<V,V,V> f)

 Map - 모든 요소에 작업 action 수행

void forEach(BiConsumer<K,V> action)

 Map - 모든 요소에 치환작업 f 수행

void replaceAll(BiFunction<K,V,V> f)


import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class Ex05 {
	public static void main(String[] args) {
		ArrayList<Integer> list = new ArrayList<>();
		for(int i = 0; i < 10; i++) {
			list.add(i);
		}
		
		list.forEach(i -> System.out.print(i + ", ")); //list의 모든 요소 출력
		System.out.println();
		
		list.removeIf(x -> x%2 == 0 || x%3 == 0); //list에서 2 또는 3의 배수 제거
		System.out.println(list);
		
		list.replaceAll(i -> i * 10); // list의 각 요소에 *10
		System.out.println(list);
		
		Map<String, String> map = new HashMap<>();
		map.put("1번", "1");
		map.put("2번", "2");
		map.put("3번", "3");
		map.put("4번", "4");
		
		map.forEach((k,v) -> System.out.print("{" + k + ", " + v + "}" + "\n"));
		
	}
}

// 메서드 참조

- 하나의 메서드만 호출하는 람다식은 메서드 참조로 간단히 할 수 있음.

① static 메서드 참조

(x) -> Class명.method(x)

Class명::method

② 인스턴스 메서드 참조

(obj, x) -> obj.method(x)

Class명::method

③ 특정 객체 인스턴스 메서드 참조

(x) -> obj.method(x)

obj::method


④ 생성자의 메서드 참조

Supplier<MyClass> s = () -> new MyClass();

Supplier<MyClass> s = MyClass::new;

 

 


import java.util.function.Function;

public class Ex06 {
	public static void main(String[] args) {
//		Function<String,Integer> f = (String s) -> Integer.parseInt(s);
		Function<String,Integer> f = Integer::parseInt; 
		System.out.println(f.apply("100") + 200);
	}
}

- 생성자의 메서드 참조

import java.util.function.Supplier;

public class Ex07 {
	public static void main(String[] args) {
		Supplier<MyClass> s = MyClass::new;
		
		System.out.println(s.get());
	}
}

class MyClass {	}



import java.util.function.Supplier;

public class Ex07 {
	public static void main(String[] args) {
		Supplier<MyClass> s = () -> new MyClass();
		Supplier<MyClass> s = MyClass::new;
		
		MyClass mc = s.get();
		System.out.println(mc);
		System.out.println(s.get());
	}
}


class MyClass {
	int iv;
	
	MyClass(int iv) {
		this.iv = iv;
	}
}

 

import java.util.function.Function;

public class Ex07 {
	public static void main(String[] args) {
		Function<Integer,MyClass> f = MyClass::new;
		
		MyClass mc = f.apply(100);
		System.out.println(mc.iv);
		System.out.println(f.apply(200).iv);
	}
}

class MyClass {
	int iv;
	
	MyClass(int iv) {
		this.iv = iv;
	}
}

'자바(JAVA)' 카테고리의 다른 글

[자바(Java)] 문자열 <-> 배열 변환  (0) 2022.12.31
[자바(Java)] 스트림(stream)  (0) 2022.12.20
[자바(Java)] 쓰레드(Thread)  (0) 2022.12.18
[자바(Java)] 애너테이션  (0) 2022.12.17
[자바(Java)] 열거형(enum)  (0) 2022.12.17