앞서 람다를 왜쓰는지 Version1부터 6까지 알아왔다 이번엔 람다를 어떻게 작성하는지 람다는 어디에 쓰이는지에 대해서 알아보겠다
람다 작성법
(파라미터) -> {람다 바디}
람다는 파라미터와 화살표 바디 이렇게 세개로 이루어져 있다 이전에 사용하던 익명 클래스를 단순화 하여 나타낸 것이다
저번 글에 Version4를 보면 사용하는 예제가 있을 것이다
기억이 나지 않는다면 아래 링크를 가서 확인해보면 되겠다
람다 사용 예제
몇가지 규칙이 있긴한데 일단 이런게 있다고 보고 몇번 사용하다보면 금방 감이 올것이다
- 매개 변수 타입은 생략가능 --> 추후 설명
- 매개 변수가 하나만 있다면 괄호 생략 가능
(user) -> user.getName().equals("userB")
-->user -> user.getName().equals("userB")
- 매개 변수가 없다면 괄호 필수
() -> System.out.println("hello")
- 바디에 실행문이 하나면 중괄호, 세미콜론 생략 가능
() -> {System.out.println("hello")}
-->() -> System.out.println("hello")
- 바디에 return문만 있다면 return 생략 가능
() -> { return 1+2;}
-->() -> return 1+2
람다 사용 위치
결론부터 말하면 함수형 인터페이스(Functional Interface)에서만 사용이 가능하다
함수형 인터페이스
추상 메서드를 한 개만 가지는 인터페이스이다
상속으로 받아서도 안되고 아예 없어서도 안된다 오직 한 개 그 이상도 이하도 안된다
추가적으로 default메서드는 이미 구현이 되어있기 때문에 몇개가 있더라도 상관이 없다
앞서 본 것과 같이 UserPredicate는 test라는 메서드 하나만을 가지기 때문에 함수형 인터페이스라고 할 수 있다
함수형 인터페이스 종류
Predicate<T>
T타입의 객체를 받아서 boolean으로 반환한다
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
아 참고로 @FunctionalInterface
이 어노테이션은 함수형 인터페이스라고 알려주는 어노테이션이다
Consumer<T>
T타입의 객체를 받아서 void로 반환한다
말그대로 객체를 소비하는 역할을 하는 것이다
@FunctionalInterface
public interface Consumer<T>{
void accept(T t);
}
Function<T, R>
T타입의 객체를 받아서 R타입으로 반환한다
@FunctionalInterface
public interface Function<T, R>{
R apply(T t);
}
Supplier<T>
void를 받아서 T타입으로 반환한다
@FunctionalInterface
public interface Supplier<T>{
T get();
}
기본형 특화 인터페이스
지금까지 확인 한것은 제네릭 함수형 인터페이스라고 한다
우선 자바에는 참조형(Reference Type)과 기본형(Primitive Type)이 있다
참조형에서 기본형으로 변환하는 것을 박싱 기본형에서 참조형으로 변환하는 것을 언박싱이라고 하는데 자동으로 변환해주는 오토박싱이라는 기능을 제공한다
우리가 흔히 사용하는 `List list`에서 `list.add(2)`와 같이 기본형 int값을 넣을 수 있는 것도 이 오토박싱이라는 기능 덕분인데 이 과정에는 메모리 탐색과정과 같은 많은 비용이 소모되기 때문에 이러한 오토박싱을 피하기 위해서 기본형 특화 인터페이스를 제공한다
Predicate
- IntPredicate
- LongPredicate
- DoublePredicate
Consumer
- IntConsumer
- LongConsumer
- DoubleConsumer
Function
- IntFunction
<R>
- IntToLongFunction
- IntToDoubleFunction
Function에는 Long타입을 변환하거나 Double타입을 변환하는 다른 것들이 있지만 형태는 위의 것들과 비슷하기 때문에 넘어가도록 하겠다 직접 쳐보면 금방 알게 될 것이다
Supplier
- IntSupplier
- LongSupplier
- DoubleSupplier
- BooleanSupplier
형식 추론
앞서 람다식을 작성할 때 파라미터의 타입은 생략이 가능하다고 했는데 이는 형식 추론덕분이다
형식 추론은 컴파일러가 대상 형식을 이용해서 람다의 시그니처(바디에 적은 실행문?)도 추론을 할 수 있기 때문에 타입은 생략이 가능하다
지역변수 사용
람다에서 사용되는 지역 변수는 final처럼 취급되어야 한다
값이 두번이상 할당이 되게 되면 컴파일 에러가 나서 실행이 불가능해 진다
메서드/생성자 참조
가독성을 높이기 위해서 람다식에서 User::getRole
처럼 메서드를 참조하여 사용을 할 수 있다
private static void methodReference() {
users.sort((user1, user2) -> user1.getAge() - user2.getAge());
users.sort(Comparator.comparing(User::getAge));
}
위 두개의 코드는 users를 나이를 기준으로 정렬을 하는 같은 역할을 한다
물론 위의 코드중 첫번째는
users.sort(new Comparator<User>() {
@Override
public int compare(User user1, User user2) {
return user1.getAge() - user2.getAge();
}
});
이 코드를 람다식으로 표현을 한 것이고 아래 코드는 comparing메서드에 인자값으로 Function을 넘겨 준것이다
public static <T, U extends Comparable<? super U>> Comparator<T> comparing( Function<? super T, ? extends U> keyExtractor) {
return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
comparing 추가 자료1
comparing 추가 자료2
생성자 참조를 위해서는 User::new
와 같이 작성을 하면된다
'공부기록 > Java' 카테고리의 다른 글
System.setIn() (0) | 2022.08.22 |
---|---|
람다 (0) | 2022.04.16 |
제네릭(2) (0) | 2021.08.09 |
제네릭(1) (0) | 2021.08.07 |
스레드(3) (0) | 2021.08.07 |