서론
바로 앞 Comparator, Comparable에 대해 알아보며, 람다식, 함수형 인터페이스에 대해서 알게 되었다.
이번에는 이에 대해서 알아보자
람다식 (Lambda Expression)
- 함수를 하나의 식으로 표현한 것이며, 함수를 람다식으로 표현하면 메서드 이름이 필요없기 때문에,
익명 함수의 한 종류라고 볼 수 있다.
장점
- 코드의 간결성
: 불필요한 반복문의 삭제가 가능, 복잡한 식을 단순하게 표현 가능 - 필요한 정보만을 사용하는 방식을 통한 퍼포먼스 향상
: 지연 연산을 지원하는 방식을 통하여 효율적인 퍼포먼스 기대 가능
단점
- 어떤 방법으로 작성해도 모든 원소를 전부 순회하는 경우 람다식이 조금 느릴 수 밖에 없다.
- 익명함수의 특성상 함수 외부의 캡처를 위해 캡처를 하는 시간제약, 논리제약적인 요소도 고려해야 하며,
디버깅 시 함수 콜스택 추척이 극도로 어렵다. - 남용하면 코드를 이해하기 어려울 수 있다. (따라서, 람다식 사용 시 주석을 다는 것이 권장됨.)
함수형 인터페이스 (@FunctionalInterface)
- 자바에서 함수형 인터페이스란 1개의 추상메서드를 가지는 인터페이스를 말한다.
- 자바의 람다 표현식은 함수형 인터페이스로만 사용이 가능하다.
(즉, 함수형 인터페이스만 람다식으로 표현할 수 있다.)
람다 표현식 문법
람다 표현식은 기본적으로는 아래와 같은 구조를 가진다.
(매개변수1, 매개변수2, ...매개변수 k) -> {함수 몸체}
이를 바탕으로 각 케이스별로 작성할 때 유의사항과 사용법을 알아보도록 하자.
1. 매개변수 X, 리턴값 X
public class Main {
public static void main(String[] args) {
Point p1 = new Point(1,2);
Point p2 = new Point(2,1);
Point p3 = new Point(1,1);
TestLambda1 t1 = ()->{
System.out.println("hello");
};
TestLambda1 t2 = ()-> System.out.println("hello");
t1.test(); //hello 출력
t2.test(); //hello 출력
}
}
@FunctionalInterface
interface TestLambda1{
abstract void test();
}
TestLambda1 함수형 인터페이스에 test라는 추상 메서드가 있을때 위와 같이 사용할 수 있다.
- 매개변수가 없을 경우 ()를 꼭 작성해주어야 한다.
- 중괄호 (= { } )로 함수 몸체를 감싸는 경우 중괄호 끝에 세미콜론( = ; ) 을 붙여야 한다.
- 함수의 몸체가 하나의 명령문으로 이루어진 경우에는 괄호를 생략할 수 있다.
2. 매개변수 O, 리턴값 X
public class Main {
public static void main(String[] args) {
Point p1 = new Point(1,2);
Point p2 = new Point(2,1);
Point p3 = new Point(1,1);
TestLambda2 t1 = (a) -> {
System.out.println("x = " + a);
};
TestLambda2 t2 = (int a) -> System.out.println("x = " + a);
TestLambda2 t3 = (a) -> System.out.println("x = " + a);
TestLambda2 t4 = a -> System.out.println("x = " + a);
t1.test(p1.x);
t2.test(p1.x);
t3.test(p1.x);
t4.test(p1.x);
}
}
@FunctionalInterface
interface TestLambda2{
abstract void test(int a);
}
TestLambda2 함수형 인터페이스에 매개변수가 정수형 1개, 반환이 없는 추상 메서드를 만들었다.
이를 4가지 방법으로 작성할 수 있으며 이에 대한 유의사항은 아래와 같다.
- 매개변수의 타입을 추론할 수 있는 경우에는 타입을 생략할 수 있다.
- 매개변수가 하나면 괄호 (= ( ) )는 생략할 수 있다.
3. 매개변수 O, 리턴값 X
public class Main {
public static void main(String[] args) {
Point p1 = new Point(1,2);
Point p2 = new Point(2,1);
Point p3 = new Point(1,1);
TestLambda3 t1 = () -> {
return 123;
};
TestLambda3 t2 = () -> {return 123;};
TestLambda3 t3 = () -> 123;
System.out.println(t1.test()); //123 출력
System.out.println(t2.test()); //123 출력
System.out.println(t3.test()); //123 출력
}
}
@FunctionalInterface
interface TestLambda3{
abstract int test();
}
TestLambda3에 매개변수가 없고, 반환형이 정수형인 추상 메서드를 만들었다.
- 함수의 몸체에 return만 있으면 중괄호 (= { } )와 return 생략 가능
4. 매개변수 O, 리턴값 O
public class Main {
public static void main(String[] args) {
Point p1 = new Point(1,2);
Point p2 = new Point(2,1);
Point p3 = new Point(1,1);
TestLambda4 t1 = (a,b,c) -> {
return a+b+c;
};
TestLambda4 t2 = (a,b,c) -> {
int tmp = a + b + c;
return tmp;
};
TestLambda4 t3 = (a,b,c) -> {return a+b+c;};
TestLambda4 t4 = (a,b,c) -> a+b+c;
System.out.println(t1.test(p1.x,p2.x,p3.x));
System.out.println(t2.test(p1.x,p2.x,p3.x));
System.out.println(t3.test(p1.x,p2.x,p3.x));
System.out.println(t4.test(p1.x,p2.x,p3.x));
}
}
@FunctionalInterface
interface TestLambda4{
abstract int test(int a, int b, int c);
}
TestLambda3에 매개변수가 없고, 반환형이 정수형인 추상 메서드를 만들었으며, 위와 같이 사용할 수 있다.
유의 사항 정리
- 매개변수가 없을 경우 ()를 꼭 작성해주어야 한다.
- 중괄호 (= { } )로 함수 몸체를 감싸는 경우 중괄호 끝에 세미콜론( = ; ) 을 붙여야 한다.
- 함수의 몸체가 하나의 명령문으로 이루어진 경우에는 괄호를 생략할 수 있다.
- 매개변수의 타입을 추론할 수 있는 경우에는 타입을 생략할 수 있다.
- 매개변수가 하나면 괄호 (= ( ) )는 생략할 수 있다.
- 함수의 몸체에 return만 있으면 중괄호 (= { } )와 return 생략 가능
Comparator 람다 표현식
바로 앞에 공부했었던 Comparator에 @FunctionalInterface 어노테이션이 있었다.
따라서 Comparator는 람다 표현식으로 이를 사용할 수 있다.
import java.util.Arrays;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
Point p1 = new Point(1,2);
Point p2 = new Point(2,1);
Point p3 = new Point(1,1);
Point p4 = new Point(1,-1);
Point array[] = {p1,p2,p3,p4};
Arrays.sort(array, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
//x가 같을 경우 y를 기준으로
if(o1.x == o2.x){
return o2.y - o1.y;
}
//x가 다르면 x를 기준으로
else {
return o2.x - o1.x;
}
}
});
for(Point p : array){
System.out.println(p.x + " " + p.y);
}
}
}
지난 시간에는 위와 같이 익명 클래스 객체를 사용하여 이를 구현하였다.
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Point p1 = new Point(1,2);
Point p2 = new Point(2,1);
Point p3 = new Point(1,1);
Point p4 = new Point(1,-1);
Point array[] = {p1,p2,p3,p4};
Arrays.sort(array, (e1, e2)->{
if(e1.x == e2.x){
return e2.y - e1.y;
}
else {
return e2.x - e1.x;
}
});
for(Point p : array){
System.out.println(p.x + " " + p.y);
}
}
}
이처럼 Comparator의 익명 클래스 객체를 생성하지 않고도,
람다 표현식을 통해 Arrays.sort의 Comparator 매개변수를 넘겨줄 수 있게 된다.
결론
그런데 이번에는 람다 표현식에 대해서 공부하던 중, stream()이 이 람다 표현식과 연관이 있다는 것을 알 수 있었고,
아래와 같이 stream에 관련된 여러 함수들을 자주 보게 되면서 stream에 대해서 알아보고 싶어졌다.
따라서 다음에는 stream에 대해서 알아보도록 하겠다.
참고한 사이트 :
https://bcp0109.tistory.com/313
https://www.tcpschool.com/java/java_lambda_concept
'프로그래밍 > Java' 카테고리의 다른 글
[Java] 6. Singleton 패턴 2 (3) | 2024.01.18 |
---|---|
[Java] 5. Java IO(Input/Output + Stream) (0) | 2023.11.09 |
[Java] 4. hashCode(), equals() (feat. HashMap, HashSet) (0) | 2023.11.08 |
[Java] 3. 싱글톤 패턴 (Singleton Pattern) (0) | 2023.10.27 |
[Java] 1. Comparable vs Comparator (feat. 정렬) (0) | 2023.08.04 |