lambda (람다, 표현식, 함수형 인터페이스, default 메소드, 메소드 레퍼런스)
참조문서
- https://docs.oracle.com/javase/8/docs/api/java/util/function/package-frame.html
- https://skyoo2003.github.io/post/2016/11/09/java8-lambda-expression
- https://homoefficio.github.io/2017/02/19/Java8-%EB%9E%8C%EB%8B%A4-%EA%B4%80%EB%A0%A8-%EC%8A%A4%ED%8E%99-%EC%A0%95%EB%A6%AC/
1. 람다란?
람다란 코드블록이다. 기존에는 코드블록은 반드시 메서드 내에 존재해야 했다. 코드블록을 가지려면 메소드, 클래스를 선언해야 했다. 하지만 자바8부터는 람다를 통해 코드블록만 가질 수 있도록 한 것이다. (js function의 1급객체같은 느낌?) 또한 이것은 코드블록을 변수처럼 사용가능하게 한다.
2. 표현식
(int a, int b) -> {return a + b} // 매개변수 -> 함수 로직 (+@ 리턴)
- 단순한 람다 구문의 경우, 람다 구분에 중괄호가 없을 수도 있다.
- return 이 없을 수도 있다.
- 매개변수에는 타입을 명시하지 않아도 된다.
- 람다식 문법을 컴파일러가 익명 클래스로 변환한다. 즉, 함수형 인터페이스를 컴파일러가 구현하도록 위임하는 형태라 볼 수 있다
표현식 예제
() -> {} // No parameters; result is void
() -> 42 // No parameters, expression body
() -> null // No parameters, expression body
() -> { return 42; } // No parameters, block body with return
() -> { System.gc(); } // No parameters, void block body
() -> {
if (true) { return 12; }
else { return 11; }
} // Complex block body with returns
(int x) -> x+1 // Single declared-type parameter
(int x) -> { return x+1; } // Single declared-type parameter
(x) -> x+1 // Single inferred-type parameter
x -> x+1 // Parens optional for single inferred-type case
(String s) -> s.length() // Single declared-type parameter
(Thread t) -> { t.start(); } // Single declared-type parameter
s -> s.length() // Single inferred-type parameter
t -> { t.start(); } // Single inferred-type parameter
(int x, int y) -> x+y // Multiple declared-type parameters
(x,y) -> x+y // Multiple inferred-type parameters
(final int x) -> x+1 // Modified declared-type parameter
(x, final y) -> x+y // Illegal: can't modify inferred-type parameters
(x, int y) -> x+y // Illegal: can't mix inferred and declared types
3. 함수형 인터페이스 @FunctionalInterface
단 하나의 추상메소드를 갖고있는 인터페이스를 함수형 인터페이스라고 부를 수 있다. @FunctionalInterface 를 붙이면 컴파일러에게 명시적으로 함수형 인터페이스임을 알려주고, 해당 인터페이스가 함수형 인터페이스 명세를 어기면 컴파일러 에러를 발생시킨다. 람다는 이 인터페이스를 통해 타입추정이 가능해진다.
4. 함수형 인터페이스 API
주로 util.function 패키지에 있다. @FunctionalInterface 를 통해 사용자 정의 함수형 인터페이스를 만들어서 사용할 수도 있지만, java8 api에서 기본적으로 지원해준다.
인터페이스명 | 메소드명 | 설명 |
Runnable | void run() | 실행할 수 있는 인터페이스 |
Supplier | T get() | 제공할 수 있는 인터페이스 |
Consumer | void accept(T t) | 소비할 수 있는 인터페이스 |
Function<T, R> | R apply (T t) | 입력을 받아서 출력할 수 있는 인터페이스 |
Predicate | Boolean test(T t) | 입력을 받아 참, 거짓을 판단할 수 있는 인터페이스 |
UnaryOperator | T apply(T t) | 단항 연산할 수 있는 인터페이스 |
5. 인터페이스 내 default 메소드, static 메소드
자바 8이전에 인터페이스는 정적 상수와 추상메소드만을 가질 수 있었다. 하지만 함수형인터페이스 등이 도입되면서 큰 변화가 있었는데, 이젠 메소드 body까지 가진 default 메소드와 정적메소드를 넣을 수 있는 것이다. 이 메소드들은 오버라이딩할 필요가 없이, implements 해주면 그냥 사용할 수 있다.
이것은 스트림 도입, 컬렉션 API를 수정하면서 forEach 등 많은 메소드들이 인터페이스에 추가되었는데, 하위 자바버전(7이하)에서 깨지는 것을 방지하기 위해 도입된 것이다.
default 메소드 선언
public default 반환형 메소드명(파라미터) {
// 비즈니스 로직
}
static 메소드 선언
public static 반환형 메소드명(파라미터) {
// 비즈니스 로직
}
다중상속에서 충돌나면?
두 인터페이스를 상속받고, 각 인터페이스는 default 메소드명가 같다. 이럴 경우 충돌이 발생한다. 이럴 때는 해당 메소드를 오버라이드하거나, 하나의 인터페이스 메소드를 선택해서 super해야만 한다. (ex. return 하나의 인터페이스명.super.충돌난 메소드명();))
6. 메소드 직접 참조
예를들어 userList를 println으로 모두 찍어낸다고 하면, userList.forEach(user -> System.out.println(user)); 가 될 것이다. 이 코드의 콜스택을 보면 ArrayList.forEach -> Consumer.accept -> System.out.println 이다. 그런데 Consumer.accept는 사실 필요가 없으며, 콜스택만 깊어지게 한다.
이럴 때 사용하는 것이 메소드 레퍼런스이다. 적용하면 userList.forEach(System.out::println); 가 된다.
6-1. 메소드 레퍼런스의 유형
- 인스턴스::인스턴스메소드 : 인스턴스 메소드의 인자가 된다.
- number -> System.out.println(number)
- System.out.println(number)
- 클래스::정적메소드 : 정적메소드의 인자가 된다.
- number -> Math.sqrt(number)
- Math::sqrt
- 클래스::인스턴스메소드 : 첫번째 인자는 인스턴스가 되고, 두번째 인자부터는 첫번째 인자 인스턴스의 메소드의 파라미터로 넘어간다.
- (a, b) -> a.compareTo(b)
- Integer::compareTo
6-2. 생성자 레퍼런스
이와 비슷하게 생성자 레퍼런스도 있다. 클래스::new 이다. 이것은 함수형 인터페이스 구현 객체를 생성해준다.
// 에러 발생
MyObj myObj = MyObj::new;
// 가능
Supplier<MyObj> myObjFactory = MyObj::new;
MyObj myObj1 = myObjFactory.get();
출처: https://sjh836.tistory.com/160?category=679845 [빨간색코딩]
'JAVA > Java' 카테고리의 다른 글
[자바 프로그래밍] 자바 this를 파헤쳐보자 (0) | 2021.03.29 |
---|---|
[Java] Java Optional (자바 옵셔널) 정리, 예제모음 (0) | 2021.03.29 |
[Java] Java 클래스 로딩 과정(Java Class Loading Process) (0) | 2021.03.29 |
Enum 찾기의 달인 (효율적으로 찾기, spring bean과 맵핑) (0) | 2021.03.21 |
객체지향 설계 5원칙 SOLID (SRP, OCP, LSP, ISP, DIP) (0) | 2021.03.21 |
객체지향의 4대 특성 (클래스, 객체, 인스턴스, 캡슐화, 상속, 추상화, 다형성) (0) | 2021.03.21 |
스레드덤프 (thread dump) (0) | 2021.03.21 |
중첩클래스를 알아보자 (내부클래스, 정적 중첩클래스, 지역클래스, 익명클래스) (0) | 2021.03.21 |