java

[java] Lambda 람다란?

우주유령 2022. 6. 10. 09:58
728x90
반응형

람다함수

람다함수는 메소드를 하나의 식(Expression)으로 표현하는 것으로 익명함수(Anonymous function)를 생성 한다.

함수형 프로그래밍을 하고자 하며 나왔다고 한다.

 

자바에서는 메소드 혼자 선언해서 쓸 수 없다. 무조건 class의 구성멤버로 선언되어야 한다.

람다식으로 생성되는 것은 메소드 자체가 아니라 실행문(메소드)를 가진 객체이다. 이 객체는 일반적인 객체가 아닌 함수형 인터페이스를 구현한 익명 구현 객체이다.

 

람다의 특징

1. 익명함수

  • 함수 이름이 없다. 익명함수는 일급객체(first class citizen)이다.

2. 커링

  • 두개 이상의 입력이 있는 함수는 최종적으로 1개의 입력만 받는 람다로 단순화 할 수 있다.

 

람다의 표현식

1. 화살표로 표시한다.

2. 몸체가 한줄이면 { } 생략 가능

3. 몸체에 return만 있으면 return생략 가능

(x, y) -> {
	// do something
}

() -> {return 1;}
() -> 1

 

함수형 인터페이스

람다는 함수형 인터페이스를 구현한다.

 

함수형 인터페이스란,

메소드가 하나밖에 없는 인터페이스를 말한다.

이를 명시적으로 선언하기 위해 @FunctionalInterface를 써줄 수 있다.

@FunctionalInterface를 써주면 메소드가 여러개일경우 오류를 내준다.

@FunctionalInterface
public interface Calculator {
	int calc(int n);
}

 

Calculator를 객체화 할 때 두가지 방법을 쓸 수 있다.

1. 이전까지 쓰던데로, implements를 통해 class 생성

2. Lambda를 이용해 익명 클래스로 생성

 

이전에는 아래와 같이 사용했다. 인터페이스의 추상메소드를 생성과 동시에 구현할 수 있다. (익명객체 참고)

package lambda;

public class Main {
	public static void main(String[] args) {
		// 인터페이스의 추상메소드를 생성과 동시에 
            	// { @Override ... } 를 통해 구현할 수 있다.
		Calculator cal = new Calculator() {
			@Override
			public int calc(int n) {
				return 0;
			}
		};
	}
}

 

람다는 이것을 간단하게 나타낼 수 있다.

package lambda;

public class Main {
	public static void main(String[] args) {
		int n=2;
		Calculator cal2 = num -> num + 1; 
        	// 타입을 추론할 수 있으므로 생략 가능
		System.out.println(cal2.calc(n)); 
        	// 3
        
        	Calculator cal3 = num -> num - 1;
		System.out.println(cal3.calc(n)); 
        	// 1
	}
}

코드가 훨씬 깔끔해 진 것을 볼 수 있다.

 

람다식은 위와 같이 사용하기 때문에 타겟이 되는 인터페이스에 추상 메소드가 반드시 하나인 Functional Interface여야 한다. 추상메소드가 2개 이상이면, 해당 람다식이 어떤 메소드를 구현한 것인지 알 수 없게 되기 때문이다.

 

Method Reference

이미 구현되어있는 메소드를 참조해 불필요한 매개변수를 제거한다.

람다식을 아래와 같이 간추릴 수 있다.

package lambda;

public interface Operator {
	public int run(int x, int y);
}
package lambda;

public class Main {
	public static void main(String[] args) {
		
		int num1 = 1, num2 = 2;
		Operator op1 = (x, y) -> Math.max(x, y);
		System.out.println(op1.run(num1, num2));
		
		Operator op2 = Math::max;
		System.out.println(op1.run(num1, num2));
		
		
	}
}

메소드 레퍼런스에는 4가지 방법이 있다.

1. 정적 메소드 참조

2. 인스턴스 메소드 참조

3. 생성자 참조

 

위의 코드는 1번의 방법이라고 할 수 있다.

2번, 3번은 아래와 같다.

// 2
(x,y) -> 인스턴스.메소드(x,y)
인스턴스::메소드

//3
(객체) -> new 객체
객체::new

 

java.util.function 패키지

지금까지와 같이 interface를 구현할 수도 있지만, 이미 구현되어있는 interface들이 많이 있다. 이것만 사용해도 거의 모든 상황을 커버할 수 있다.

 

Predicate, Consumer, Supplier, Function<T,R>, Comparator, Runnable, Callble등이 있다. 더 있으니 java8 api를 뒤져보자.

package lambda.predicate;

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

public class Ex {
	public static void main(String[] args)
    {
		// Predicate는 무언가 비교할 때 쓴다. 
        	// test메소드는 Boolean을 반환한다.
		Predicate<Integer> lesserthan = i -> (i < 10);
		Boolean res = lesserthan.test(3);
		System.out.println(res);
		
		// Consumer는 말그대로 소비만 한다. 
       	 	// accept메소드는 리턴 타입 void이다.
		Consumer<Integer> print = i -> System.out.println(i * 10);
		print.accept(7);
    }
}

 

 

 

 

 

 

 

참고

익명객체 https://limkydev.tistory.com/226

Predicate https://www.geeksforgeeks.org/java-8-predicate-with-examples/

 

 

 

 

728x90
반응형