Java Lambda 관련 정리 from 이것이 자바다(책)

Updated:

Java Lambda

Lambda : 익명 함수(anonymous function)을 생성하기 위한 식으로, 객체 지향 언어보다는 함수 지향 언어에 가까움

그런데 왜 자바에서 이런 람다식을 수용한 것인가?

  1. 자바 코드가 매우 간결해짐
  2. 컬렉션의 요소를 필터링하거나 매핑해서 결과 집계를 쉽게 가능하게 함
 // 람다식 -> 매개 변수를 가진 코드 블록 -> 익명 구현 객체

Runnable runnable = new Runnable() { // new 부터의 형태가 익명 구현객체이다
		@Override
		public void run() {
			//어쩌고..
		}
};

// 그러나 람다식으로 가게 되면

Runnable runnable = () -> {...};

기본 문법

// (타입 매개변수, ...)->{실행문; ...}
(int a) -> {System.out.println(a);}
// 매개변수타입은 런타임시에 자동으로 인식되어서, 일반적으로는 매개변수타입은 넣지 않는다
(a)->{System.out.println(a);}
// 매개변수가 1개이고, 만약 실행문이 한개면 ()괄호와 {}괄호는 생략이 가능하다
a->System.out.println(a);
(x,y)->{return x+y;} // 만약 리턴문이 있는데, 리턴문만 있다면
(x,y)-> x+y ; // 로 바꿀수있다.

람다식은 하나의 메소드를 정의하기 때문에, 두 개 이상의 추상 메소드가 선언된 인터페이스는 람다식을 이용해서 구현 객체를 생성할수 없다. 그래서 하나의 추상 메소드만 가능하다. (여기서 착각하지 말아야 할 부분은 디폴트 메소드나 정적 메소드는 포함이 되지않는다. 즉, 디폴트 메소드와 정적 메소드는 몇개 이상이 추가되어도 상관이 없다는 말임)

이 때, 함수적 인터페이스를 작성할때, 두 개 이상의 추상 메소드가 선언되지 않도록 컴파일러가 체킹해주는 기능이 있다. @Functional 이라는 annotation을 붙이면 된다

@FunctionalInterface
public interface MyFunctionalInterface {
	public void method();
	public void otherMethod(); // 컴파일 에러

}

함수적 인터페이스는 크게 Consumer, Supplier, Function, Operator, Predicate로 구분된다. 구분 기준은 인터페이스에 선언된 추상 메소드의 매개값과 리턴값의 유무이다.

Lambda_대표적인터페이스

저 인터페이스들을 이용해서 그냥 각각의 경우만 처리하면 그닥 큰 쓸모가 없을수도 있다. 그래서 Consumer, Function, Operator 종류의 함수적 인터페이스는 andThen()과 compose() 디폴트 메소드를 가지고 있다.

먼저 andThen()의 경우는, 첫번째 인터페이스의 일을 먼저 처리한후, 두 번째 매개값으로 제공해 최종 결과를 얻을 때 사용

인터페이스AB = 인터페이스A.andThen(인터페이스B); // 인터페이스A의 메소드를 먼저 실행한뒤에 인터페이스 B의 메소드를 실행
최종결과 = 인터페이스AB.method(); 

인터페이스AB = 인터페이스A.compose(인터페이스B); // 인터페이스B의 메소드를 먼저 실행한뒤에 인터페이스 A의 메소드를 실행
최종결과 = 인터페이스AB.method();

Lambda_andThen_compose

메소드 참조(Method References) : 말 그대로 메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내어, 람다식에서 불필요한 매개 변수를 제거하는 것이 목적이다.

(left,right) -> Math.max(left,right); // Lambda Expression
Math::max; // Method References

메소드 참조도 람다식과 마찬가지로 인터페이스의 익명 구현 객체로 생성이 된다.

클래스이름 :: 메소드 // static(정적) 메소드를 참조할 경우
참조변수 :: 메소드 // 인스턴스 메소드를 참조할경우, 저기서 참조변수는 사실상, new 연산자로 할당된 인스턴스 이름을 의미할것이다.
클래스 :: new // (a,b) -> return new 클래스(a,b); 만약 해당 생성자가 존재하지 않으면 컴파일 에러!!

위와 관련한 모든 예제들은 아래 링크 주소에 있으니 참고해주시면 감사하겠습니다.
https://github.com/goodgood619/ThisIsJava

Leave a comment