Home 람다(Lambda)
Post
Cancel

람다(Lambda)

람다식(Lambda expression)

람다식의 도입으로 이해 자바는 객체지향언어인 동시에 함수형 언어가 되었다.

람다식은 간단히 말해서 메서드를 하나의 식으로 표현한 것이다. 람다식은 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해준다.

메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로, 람다식을 ‘익명함수’라고도 한다.

람다식은 ‘익명함수’ 답게 메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통{} 사이에 ‘ -> ‘ 를 추가한다.

ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//메서드
int max(int a, int b) {
	return a > b ? a : b;
}

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

int roll(){
	return (int) (Math.random() * 6);
}

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

(String name, int i) -> System.out.println(name+"="+i);

() -> (int) (Math.random * 6)
  1. 람다식에 선언된 매개변수의 타입은 추론이 가능한 경우 생략할 수 있다. 대부분의 경우 생략이 가능하다. 람다식에 반환타입이 없는 이유도 항상 추론이 가능하기 때문이다.

  2. 매개변수가 하나뿐인 경우 괄호()를 생략할 수 있다. 단 매개변수의 타입이 있으면 괄호()를 생략할 수 없다.

  3. 괄호{} 안의 문장이 하나일 때는 괄호{}를 생략할 수 있다. 이 때 문장의 끝에 ‘;’는 붙이지 않아야 한다. 단 괄호{}안의 문장이 return 문일 경우 괄호{}를 생략할 수 없다.

함수형 인터페이스

람다식이 메서드와 동등한 것 같지만 익명 클래스의 객체와 동등하다.

ex)

1
2
3
4
5
6
7
(int a, int b) -> a > b ? a : b

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

람다식으로 정의된 익명 객체의 메서드를 어떻게 호출할 수 있나? 알고 있듯 참조변수가 있어야 객체의 메서드를 호출 가능하다.

1
타입 f = (int a, int b) -> a > b ? a : b

이 때 참조변수 f의 타입은 어떤 것이어야 하는가. 참조형이기때문에 클래스 또는 인터페이스가 가능하다. 그리고 람다식과 동등한 메서드가 정의되어 있는 것이어야 한다. 그래야 참조변수로 람다식의 메서드를 호출할 수 있기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface MyFunction{
	public abstract int max(int a, int b);
}

MyFunction f = new MyFunction() {
					public int max(int a, int b) {
						return a > b ? a : b;
					}
				};
int big = f.max(5, 3);


Myfunction f = (int a, int b) -> a > b ? a : b;
int big = f.max(5, 3);	
1
2
3
4
5
6
7
8
9
10
11
List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");

Collections.sort(list, new Comparator<String>() {
	public int compare(String s1, String s2) {
		return s2.compareTo(s1);
	}
});

//위를 람다식으로 간단히 처리 가능하다.

Collections.sort(list, (s1, s2) -> s2.compareTo(s1));

함수형 인터페이스가 아래와 같이 구현되어 있을 때.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@FunctionalInterface
interface MyFunction{
	void run();
}

class LambdaEx1{
	static void execute(MyFunction f){
		f.run();
	}
	
	static MyFunction getMyFunction(){
		MyFunction f = () -> System.out.println("f3.run()");
		return f;
	}

	public static void main(String[] args){
		MyFunction f1 = () -> System.out.println("f1.run()");
		
		MyFunction f2 = new MyFunction(){
			public void run(){
				System.out.println("f2.run()");
			}
		};

		MyFunction f3 = getMyFunction();

		f1.run();
		f2.run();
		f3.run();

		execute(f1);
		execute( ()->System.out.println("run()") );
		//참조변수 없이 직접 람다식을 매개변수로.
	}
//f1.run()
f2.run()
f3.run()
f1.run()
run()

메서드의 매개변수가 MyFunction타입이면, 이 메서드를 호출할 때 람다식을 참조하는 참조변수를 매개변수로 지정해야 한다는 뜻이다. 또는 참조변수 없이 직접 람다식을 매개변수로 지정하는 것도 가능하다.

람다식을 참조변수로 다룰 수 있다는 것은 메서드를 통해 람다식을 주고받을 수 있다는 것을 의미한다. 즉, 변수처럼 메서드를 주고 받는 것이 가능하다는 뜻이다.

람다식의 타입과 형변환

함수형 인터페이스로 람다식을 참조할 수 있는 것일 뿐, 람다식의 타입이 함수형 인터페이스의 타입과 일치하는 것은 아니다. 람다식은 익명 객체이고 익명 객체는 타입이 없다. 정확히는 타입은 있지만 컴파일러가 임의로 이름을 정하기 때문에 타입을 알 수 없다. 그래서 대입 연산자의 양변의 타입을 일치시키기 위해 아래와 같이 형변환이 필요하다.

1
MyFunction f = (MyFunction) ( ()->{} );

람다식은 오직 함수형 인터페이스로만 형변환이 가능하다.

컬렉션 프레임웍 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class LambdaEx{
	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+",");
		System.out.println();

		list.removeIf(x-> x%2==0 || x%3==0);
		System.out.println(list);

		list.replaceAll(i->i*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+"},"));
		//map의 모든 요소를 {k,v} 형태로 출력
	}
This post is licensed under CC BY 4.0 by the author.