-
[JAVA] 람다식 (Lambda expression)DEV/JAVA 2024. 3. 17. 17:57
* 람다식 : 메서드를 하나의 식으로 표현한 것
→ 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해줌
→ 메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어져 람다식을 익명함수라고도 함
int [] arr = new int[5];
Arrays.setAll(arr, (i) -> (int) (Math.random() * 5) + 1);
위의 람다식을 메서드로 표현하면 아래와 같다.
int method() {
return (int) ((Math.random() * 5) + 1);
}
* 메서드와 비교했을때 람다식의 장점 !
- 간결하면서 이해하기 쉽다.
- 함수를 만들 필요가 없어 생산성이 좋다. (메서드는 클래스가 필요하며, 객체도 생성해야만 호출해서 사용할 수 있다.)
* 람다식 작성법
: 메서드에서 이름과 반환타입을 제거, 매개변수 선언부와 몸통 {} 사이에 ‘->’ 를 추가
- 메서드 : 반환타입 메서드이름 (매개변수) { 문장 }
- 람다식 :
반환타입 메서드이름(매개변수) -> { 문장 }ex) 아래와 같이 두 수 중 더 큰 값을 반환하는 메서드를 람다식으로 변환 한 예시
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 }
선언된 매개변수가 하나뿐인 경우에는 소괄호 생략이 가능하다.
ex ) (a) -> a * a
→ a -> a * a
마찬가지로 중괄호 {} 안의 문장도 하나라면 생략이 가능하다.
문장이 아닌 ‘식’이므로 세미콜론(;)을 붙이지 않도록 주의해야한다.
(String name, int i) -> { System.out.println(name+”=”+i); }
→ (String name, int i) -> System.out.println(name+”=”+i)
* 함수형 인터페이스 (Functional Interface)
: 모든 메서드는 클래스내에 포함되어야한다. 그렇다면 람다식은 어떤 클래스에 포함되는 것일까?
사실 람다식은 메서드가 아닌 익명클래스의 객체와 동등하다.
@FunctionalInterface interface MyFunction { public abstract int max(int a, int b); } MyFunction f = new MyFunction() { public int max (int a, int b) { return a > b ? a : b; } } int big = f.max(5,3);
위와 같이 인터페이스를 구현한 익명객체를 람다식으로 대체가 가능하다.
Myfunction f = (int a, int b) -> a > b ? a : b; int big = f.max(5,3);
→ 람다식은 본래 익명객체이고, 인터페이스를 구현한 익명객체의 메서드와 람다식의 매개변수의 타입과 개수, 반환값이 일치하면 대체가 가능하다.
→ 람다식을 다루기 위한 인터페이스를 ‘함수형 인터페이스’라고 부르기로했다.
→ 단, 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야하는 제약이 있다.
(그래야 람다식과 인터페이스의 메서드가 1:1로 연결될 수 있기 때문)
→ 반면, static 메서드와 default 메서드의 개수에는 제약이 없다.
→ 인터페이스 상단 @FunctionalInterface 어노테이션을 붙여야 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인해주므로 붙이도록한다.
* 스터디 후 Q&A 정리
1. 함수형 인터페이스 내에서 static 메서드와 default 메서드의 개수 제약이 없는 것의 의미는 무엇인가요 ?
함수형 인터페이스에는 오직 하나의 추상메서드만 정의되어야 한다. 그래야 람다식과 인터페이스의 메서드가 1:1로 연결될 수 있기 때문이다. 반면에 static메서드와 default 메서드의 개수에는 제약이 없다.
- 인터페이스 : 일종의 추상 클래스. 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다. 오직 추상메서드와 상수만을 멤버로 가질 수 있다.
- 추상클래스 : 추상 메서드를 포함하고 있는 클래스이며 생성자, 멤버변수, 일반 메서드도 가질 수 있다.
→ 인터페이스의 모든 메서드는 추상 메서드여야 하는데, JDK1.8부터 static 메서드와 디폴트 메서드의 추가를 허용하게 되었다.
- static 메서드 : 인스턴스와 관계가 없는 독립적인 메서드이기 때문에 예전부터 인터페이스에 추가하지 못할 이유가 없었으나, 자바의 규칙을 단순화 시키기위해 '인터페이스의 모든 메서드는 추상메서드이어야 한다'는 규칙에 예외를 두지 않기 위해 인터페이스와 관련된 static 메서드는 별도의 클래스에 따로 두어야 했다.
ex ) java.util.Collection 인터페이스와 관련된 static 메서드들이 인터페이스에는 추상 메서드만 선언할 수 있다는 원칙 때문에 별도의 클래스인 Collections라는 클래스에 들어가게 되었다.
- default 메서드 : 추상메서드의 기본적인 구현을 제공하는 메서드. 인터페이스에 메서드 하나를 추가하게되면 추상메서드이기때문에 해당 인터페이스를 구현한 클래스들이 새로 추가된 메서드에 대해 구현해야한다. 인터페이스가 변경되지않는것이 베스트이지만, 언젠가 변경은 발생하기 마련이다. 때문에 생긴 메서드.
2. 람다식 세미콜론 유무는 어떻게 정해지나요?
: 람다식에서 세미콜론은 문맥에 따라 생략이 가능하다. 일반적으로 람다식이 단일 표현식으로만 이루어져 있고 그 표현식이 문장을 종료하는 경우에는 세미콜론을 생략할 수 있다. 하지만 여러 문장으로 이루어진 복합 표현식을 사용할 때는 각 문장을 세미콜론으로 구분해야 한다.
Function<Integer, Integer> addOne = (x) -> x + 1;
위와 같은 단일 표현식은 세미콜론이 생략이 가능하다.
아래 코드에서는 중괄호 안에 여러 문장이 있으므로 각 문장을 세미콜론으로 구분해야 한다.
Function<Integer, Integer> addOneAndPrint = (x) -> { System.out.println("Adding one to " + x); return x + 1; };
반환값이 있는 메서드의 경우 return 문 대신 식으로 대신하여 세미콜론을 생략할 수 도 있으나,
위 예시 코드에서 처럼 여러 문장이 있을때는 문장을 식으로 변경할 수 없기에 반환 값을 명시해야하는 것은 물론 세미콜론도 생략할 수 없다.
ex) return 문을 식으로 변경 가능할 때, 생략 가능한 예시
(int a, int b) -> { return a >b ? a : b; }
→ ( a, b ) -> a > b ? a : b
* 참고자료 : Java의 정석 | 남궁성
'DEV > JAVA' 카테고리의 다른 글
[JAVA] 쓰레드(Thread)의 동기화 / synchronized / Lock과 Condition / fork & join 프레임워크 (0) 2024.04.23 [JAVA] 쓰레드 (Thread)의 구현 / 싱글쓰레드, 멀티쓰레드 / 우선순위 / 쓰레드 그룹 / 데몬 쓰레드 (2) 2024.04.05 [JAVA] 제네릭스(Generics) / 열거형 (Enum) (2) 2024.04.03 [JAVA] 스트림(Stream) 생성 / 중간연산 map,flatMap / 최종연산 reduce, collect / Optional (2) (0) 2024.03.26 [JAVA] 스트림(Stream) 기본 / 특징 / 연산 (1) (0) 2024.03.20