람다식
람다식(Lamda expression)
람다식의 도입으로 인해, 자바는 객체지향언어인 동시에 함수형 언어가 되었다.
람다식이 무엇인지 알아보도록 하자.
람다식이란?
람다식(Lamda expression)은 간단히 말해서 메서드를 하나의 '식(expression)'으로 표현한 것이다.
- 메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로, 람다식을 익명함수라고도 한다.
람다식 작성하기
람다식은 '익명함수'답게 메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통{} 사이에 '->'을 추가한다.
int max(int a, int b){
return a > b ? a : b;
}
위의 코드를 람다식으로 변경하면 아래와 같이 나타낼 수 있다.
(int a, int b) -> {return a > b ? a : b;}
- 반환값이 있는 메서드의 경우, return문 대신에 '식'으로 대신할 수 있다.
(int a, int b) -> a > b ? a : b
//이 때 문장이 아닌 '식'이므로 끝에 ';'을 붙이지 않는다.
- 람다식에 선언된 매개변수의 타입은 추론이 가능한 경우 생략할 수 있으며, 대부분의 경우에 생략 가능하다.
- 람다식에 반환타입이 없는 이유도 항상 추론이 가능하기 때문이다.
(a, b) -> a > b ? a : b
함수형 인터페이스
하나의 메서드가 선언된 인터페이스를 정의해서 람다식을 다루는 것은 기존의 자바 규칙들을 어기지 않으면서도 자연스럽다.
- 그렇기 때문에, 인터페이스를 통해 람다식을 다루기로 결정하였다.
- 람다식을 다루기 위한 인터페이스를 함수형 인터페이스(functional interface)라고 부르기로 했다.
@FunctionalInterface
interface MyFunction{ //함수형 인터페이스 MyFunction을 정의
public abstract int max(int a, int b)
}
- 단, 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야 한다는 제약이 있다.
- 그래야 람다식과 인터페이스의 메서드가 1:1로 연결될 수 있기 때문이다.
- 반면에 static 메서드와 default메서드 개수에는 제약이 없다.
📌 @FunctionalInterface을 붙이면, 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인해주므로, 꼭 붙이도록 하자.
함수형 인터페이스의 타입의 매개변수와 반환타입
함수형 인터페이스가 아래와 같이 정의되어 있을 때
@FunctionalInterface
interface MyFunction{ //함수형 인터페이스 MyFunction을 정의
void myMethod(); //추상 메서드
}
- 메서드의 매개변수가 MyFunction타입이라면, 아래와 같이 이 메서드를 호출 할 때 람다식을 참조하는 참조변수를 매개변수로 지정해야한다.
void eMethod(MyFunction f){ //매개변수의 타입이 함수형 인터페이스인 임의의 메서드 작성
f.myMethod(); //MyFunction에 정의된 메서드를 호출
}
.....
MyFunction f = () - > System.out.println("람다식 작성");
aMethod(f); //람다식을 참조하는 참조변수를 매개변수로
//또는 참조변수 없이 직접 람다식을 매개변수로 지정하는 것도 가능하다.
aMethod(() - > System.out.println("람다식 작성"));
이렇게 람다식을 참조변수로 다룰 수 있다는 것은 메서드를 통해 람다식을 주고받을 수 있다는 것을 의미한다.
람다식의 타입과 형변환
함수형 인터페이스로 람다식을 참조할 수 있는 것일 뿐, 람다식의 타입이 함수형 인터페이스의 타입과 일치하는 것은 아니다.
- 람다식은 익명객체이며, 익명객체는 타입이 없다.(컴파일러가 임의로 이름을 정하기 때문에 알 수 없다.)
- 그래서 대입 연산자의 양변 타입을 일치시키기 위해서 아래와 같이 형변환이 필요하다.
MyFunction f = (MyFunction) (() -> {}); //양변의 타입이 다르기 때문에 형변환이 필요하다.
- 이러한 형변환은 생략이 가능하며, 람다식은 함수형 인터페이스로만 형변환이 가능하다.
- 굳이 Object타입으로 형변환을 하고 싶다면 위와 같이 함수형 인터페이스로 형변환한 후 한번 더 Object 타입으로 형변환 해주어야한다.
외부 변수를 참조하는 람다식
람다식 내에서 참조하는 지역변수는 final이 붙지 않았어도 상수로 간주된다.
- 람다식 내에서 지역변수를 참조하고 있다면 람다식 내에서나 다른 곳 어디에서도 이 변수들의 값을 변경하는 일은 허용되지 않는다.
java.util.function패키지
대부분의 메서드는 타입이 비슷하다. 매개변수가 없거나 한 개 또는 두 개, 반환 값은 없거나 한개이며 제네릭 메서드로 정의하면 매개변수나 반환 타입이 달라도 문제가 되지 않는다.
- java.util.function패키지에 일반적으로 자주쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의 해놓았다.
- 매번 새로운 함수형 인터페이스를 정의하지 말고, 가능하면 이 패키지의 인터페이스를 활용하는 것이 좋다.
매개변수와 반환값의 유무에 따라 4개의 함수형 인터페이스가 정의되어 있고, Function의 변형으로 Predicate가 있는데, 반환값이 boolean이라는 것만 제외하면 Function과 동일하다.
- Predicate는 조건식을 함수로 표현하는데 사용된다.
함수형 인터페이스 | 메서드 | 설명 |
---|---|---|
java.lang.Runnable | void run() | 매개변수도 없고, 반환값도 없다. |
Supplier<T> | T get() | 매개변수는 없고, 반환값만 있다. |
Consumer<T> | void accept(T t) | Supplier와 반대로 매개변수만 있고, 반환값이 없다. |
Function<T,R> | R apply(T t) | 일반적인 함수, 하나의 매개변수를 받아서 결과를 반환한다. |
Predicate<T> | boolean test(T t) | 조건식을 표현하는데 사용되며, 매개변수는 하나. 반환타입은 boolean이다. |
- 매개변수가 두 개인 함수형 인터페이스는 이름 앞에 'Bi'가 붙는다.
참고로 컬렉션 프레임워크의 인터페이스에도 다수의 디폴트 메서드가 추가되 었는데, 그 중의 일부는 함수형 인터페이스를 사용한다.
메서드 참조
람다식이 하나의 메서드만 호출하는 경우에는 '메서드 참조(method reference)'라는 방법으로 람다식을 간략히 할 수 있다.
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
위와 같이 작성된 람다식을 메서드 참조로 나타내면?
Function<String, Integer> f = Integer::parseInt;
- 위 메서드 참조에서 람다식의 일부가 생략된 모습을 볼 수 있다.
- 컴파일러는 생략된 부분을 우변의 parseInt메서드의 선언부로부터, 또는 좌변의 Function인터페이스에 지정된 지네릭스 타입으로부터 쉽게 알아낼 수 있다.
람다식을 메서드 참조로 바꾼 예시
BiFunction<String, String, Boolean> f = (s1, s2) -> s1.equals(s2);
//메서드 참조
BinFunction<String, String, Boolean> f = String::equals;
이미 생성된 객체의 메서드를 람다식에서 사용하는 경우에는 클래스 이름 대신 그 객체의 참조변수를 적어줘야한다.
MyClass obj = new MyClass();
Function<String, Boolean> f = (x) -> obj.equals(); //람다식
Function<String, Boolean> f = obj::equals; //메서드 참조
하나의 메서드만 호출하는 람다식은 '클래스이름::메서드이름' 또는 '참조변수::메서드이름'으로 바꿀 수 있다.
생성자의 메서드 참조
생성자를 호출하는 람다식도 메서드 참조로 변환할 수 있다.
Supplier<MyClass> s = () -> new MyClass(); //람다식
Supplier<MyClass> s = MyClass::new; //메서드 참조
그리고 배열을 생성할 때는 아래와 같이 하면 된다.
Function<Integer, int[]> f = x -> new int[x]; //람다식
Function<Integer, int[]> f= int[]::new; //메서드 참조
📌 메서드 참조는 람다식을 마치 static변수처럼 다룰 수 있게 해주며, 코드를 간략히 하는데 유용하므로 많이 사용된다.
Reference
자바의 정석 3rd Edition
Author And Source
이 문제에 관하여(람다식), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@hyeming/람다식
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
자바의 정석 3rd Edition
Author And Source
이 문제에 관하여(람다식), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hyeming/람다식저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)