Java

[Java] 람다식 - Lambda Expression

디벨로펄 2022. 8. 29.
반응형

Thread에서 Runnable을 접하면서 lambda식 형태를 처음 봤었다.

이후 안드로이드 UI, 파이썬 등을 하면서 종종 봤었는데, 사용하는 방법만 알고 넘어왔기 때문에 이번 글을 통해 정리를 하려한다.

 

람다식의 도입

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

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

 

함수형 인터페이스

자바에서 모든 메서드는 클래스에 포함되어야 한다. 람다식은 익명 클래스의 객체라 보면 된다.

 

람다식은 어떻게 불러와서 사용할 수 있을까? 타입이 있어야 하지 않을까?

그 타입을 함수형 인터페이스라 보면 편하다.(조금 더 내리면  좀 더 자세한 설명 있음)

 

 

public interface MyFunction {
	public abstract int operate(int a, int b);
}

MyFunction func = new MyFunction(){
	public int operate(int a, int b){
 		return a+b;   
    }
};
System.out.println(func.operate(10,20)); // 익명 객체 메서드 호출. 

MyFunction function = (int t, int v) -> t + v; 
System.out.println(function.operate(10,20)); // 익명 객체 메서드 호출. 

MyFunction function2 = (t, v) -> t + v; // 타입 생략 가능.
System.out.println(function2.operate(10,20));

MyFunction인터페이스를 구현한 익명 개체를 람다식으로 대체 가능한 이유는, 람다식도 실제로 익명 객체이고, MyFunction메서드의 매개변수 타입과 개수, 그리고 리턴값이 일치하기 때문이다.

여기서 함수형 인터페이스는 단 하나의 추상 메서드만 정의되어 있어야 한다!

단, static Method와 default Method의 개수에는 제약이 없다.

 

*** @FunctionalInterface 어노테이션을 붙이게 되면 함수형 인터페이스 정의를 확인해준다.

두 개 이상의 추상 메서드가 정의되어 있으면 아래와 같은 메세지를 볼 수 있다. 

Invalid '@FunctionalInterface' annotation; MyFunction is not a functional interface

람다식의 Type

실제로는 람다식은 익명 객체이며, 타입이 없는데, 형변환을 통해서 함수형 인터페이스로 변환이 되는 것이다.

** 오직 함수형 인터페이스로만 형 변환이 가능하다. Object타입으로 형변환 안됨.

//System.out.println((tt, vv) -> tt + vv);// 에러
System.out.println((MyFunction) ((tt, vv) -> {
	return tt + vv;
}));
		
System.out.println((MyFunction) (tt, vv) -> tt + vv);
        
출력 결과
test.Main$$Lambda$25/0x000000080008e840@5b464ce8
test.Main$$Lambda$26/0x000000080008ec40@19dfb72a

 

java.util.function 패키지

앞에서 람다식은 함수형 인터페이스로 형 변환이 가능하다고 했다. java.util.function 패키지에서는 자주쓰는 함수형 인터페이스를 정의해두었다. Supplier, Consumer, Function, Predicate. 명칭으로부터 각 인터페이스를 유추할 수 있다.

Runnable : 입력값과, 리턴값이 모두 없음. 안에서 실행되고 끗.   run()

Supplier : 공급! 리턴값을 던져주는 녀석. get()

Consumer : 소비. 입력값을 받고 리턴값은 없는 녀석. accept()

Function : 입력에 따른 리턴이 있는 녀석 apply()

Predicate : (판단, 단정하다 라는 뜻을 가짐) 입력을 받아서 true, false를 리턴한다. test()

 

* 매개변수가 두 개일 경우 Bi가 접두사로 붙는다. ex) BiConsumer, BiFunction, BiPredicate

UnaryOperator, BinaryOperator : 입력 타입과 리턴 타입이 모두 일치한다. Unary는 1개 input, Binary는 2개 Input

 

기본형을 사용하는 함수형 인터페이스 또한 따로 존재한다.

똑같은 기능을 수행하지만 오토박싱, 언박싱이 적게 발생해서 성능이 더 좋다.(연산 속도가 더 빠르다.)

ex) DoubleToIntFunction, ToIntFunction<T>, IntUnaryOperator

→ 성능이 중요하다면 이 인터페이스를 사용하는 것이 좋을듯.

 

반응형

댓글