람다와 스트림의 사용법(1)
날짜: 2021년 6월 10일
학습목표
Java8에 추가된 람다표현식과 스트림에 대해 알아보자. Thread코딩테스트를 풀어보려 했으나, Java8문법에 익숙해 지지 않아 기본적인 코드 흐름을 해석하기 힘들었다.
이번 포스팅에서는 코딩테스트의 문법을 해석하기위해, 람다표현식과 스트림 중 기본적인
람다표현식사용법에 대해서만 알아가는 것을 목표로 한다.
- 람다표현식
람다표현식
람다표현식을 익혀 놓지 않으면, 코드를 읽는데 매우 어려움을 겪을수 있다. Java8이상을 사용한다면 반드시 알고 있어야한다. 앞서 익명 클래스에 대해서 공부해본적 이있다. 익명 클래스를 사용하면 가독성도 떨어지고 불편한데, 이런 단점을 보완한것이 람다 표현식이다.
- 람다 표현식은 인터페이스에 메소드가 "하나"인 것들만 적용 가능하다.
- java.lang.Runnable
- java.util.Comparator
- ...
다음 코드를 보며 람다표현식이란 무엇인지 이해해 보자.
(int x, int y) -> x + y
() -> 43
(String s) -> {System.out.println(s);}
기본 람다 표현식은 세 부분으로 구성되어 있다.
좌측의 매개변수들을 받아, 처리식의 결과를 리턴한다. 즉, 위의 표를 해석하면, int x, int y를 매개변수로 받아, x+y를 리턴한다는 뜻이다.
실습
다음 코드를 보며 더 쉽게 이해하자
interface Calculate{
int operation(int a, int b);
}
Calculate라는 인터페이스가 있고, operation()메소드가 선언되어있다. 대신 a와 b로 어떤 작업을 하는지는 선언되어 있지 않은 상태다. 우선 Calculate인터페이스를 앞서 배운 익명클래스로 구현해보겠다.
private void calculateClassic(){
Calculate calculateAdd = new Calculate() {
public int operation(int a, int b) {
return a+b;
}
};
System.out.println(calculateAdd.operation(1,2));
}
/*
3
*/
operation 메소드에서는 a와 b를 더해주고 리턴한다.
이를 람다표현식으로 처리하려면 어떻게 해야 할까??
private void calculateLambda() {
Calculate calculateAdd = (a,b) -> a+b;
System.out.println(calculateAdd.operation(1,2));
}
/*
3
*/
코드가 엄청나게 간단해졌다. 메소드의 첫줄을 보면, (a,b) -> a+b라고 되어있다.
a와b라는 변수가 전혀 선언 되지 않았음에도 a와b를 사용하고 심지어 그 값들을 더하고 있다. Calculate라는 인터페이스의 선언된 메소드가 하나뿐 이기 때문에,
(a,b)로 선언된 부부은 operation()메소드의 int a와 int b를 매개변수로 받는다는 의미이다.
-> 옆의 a+b는 결과로 a와b의 합을 리턴한다는 의미이다. a와b가 매개변수를 뜻하기 때문에, 임의로 선언해도 아무런 문제가 없다!
제대로 이해했는지 확인해보기 위해, calculateSubtract라는 변수를 만들고, 그 변수의 수행 결과는 빼기를 처리하는 람다 표현식을 작성해 보자!
- 실습코드
private void calculateLambda() { Calculate calculateAdd = (a,b) -> a+b; System.out.println(calculateAdd.operation(1,2)); Calculate calculateSubtract = (x,y) -> x-y; System.out.println(calculateSubtract.operation(1, 2)); } /* 3 -1 */
람다 표현식을 잘 이해했다면, 다시 Calculate 인터페이스를 보자.
interface Calculate{
int operation(int a, int b);
}
일반적인 인터페이스로 보이지만, 이 인터페이스는 Functional(기능적) 인터페이스라고 부를 수 있다. 기능적 인터페이스란 메소드가 1개만 선언되어있는 것을 말한다. 그런데, 이렇게 선언하면 혼동이 될수도 있다. 왜냐하면 같이 개발하는 사람이 이 인터페이스의 선언이 모호하다면 operationAdd() 와 operationsubstract()를 구분해 선언할수 있기 때문이다.
interface Calculate{
int operationAdd(int a, int b);
int operationSubstract(int a, int b);
}
이렇게 되면 람다 표현식을 사용할 수 없고, 람다 표현식에 컴파일 오류가 생긴다.
- FunctionalInterface Annotation
이렇게 명시적으로 Annotation을 선언하면, 메소드가 2개이상 선언될때 컴파일 오류를 출력해, 메소드를 1개로 유지할 수 있다.@FunctionalInterface interface Calculate{ int operation(int a, int b); }
- 이런 혼동을 피하기 위해서, 이런 Functional 인터페이스를 선언시 아래와 같은 Annotation을 선언할 수있다.
그럼, 마지막으로 Runnable 인터페이스를 람다 표현식으로 구현해보며, 사용법을 숙지하자.
먼저, Runnable 인터페이스를 익명클래스로 구현해보겠다.
private void runCommonThread() {
Runnable runnable = new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
new Thread(runnable).start();
}
Runnable 인터페이스는 run()메소드 하나만 선언되어 있기때문에, 람다 표현식으로 처리가 가능하다. 람다 표현식으로 구현해 보겠다.
private void runCommonThread2() {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
}).start();
}
그러나 run()메소드의 로직이 한 줄밖에 안되므로 더 줄일수도 있다.
private void runCommonThread3() {
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
}
중괄호를 없애고, getName() 뒤의 세미콜론(;)도 없앴다.
마지막으로 정리하자면,
- 메소드가 하나만 존재하는 인터페이스는 @FunctionalInterface로 선언할 수 잇으며, 이 인터페이스를 람다 표현식으로 처리할 수 있다.
- (매개 변수 목록) -> 처리식 의 형태로 람다를 표현하며, 처리식이 한 줄이상 일때는 중괄호로 묶을 수 있다.
Feedback
알게된점
- 람다표현식
알아야할 점
- -
References
자바의신 VOL2
'프로그래밍 > Java' 카테고리의 다른 글
Java I/O (0) | 2021.06.11 |
---|---|
람다와 스트림의 사용법(2) (0) | 2021.06.10 |
Thread(1) (0) | 2021.06.02 |
Set과 Queue -Reboot(4) (0) | 2021.06.02 |
캐시 교체알고리즘 과 상황별 효율성 (0) | 2021.06.01 |