자바는 어떤 기능을 구현하기 위해 메소드를 정의하여 사용한다. 그런데 이를 위해서는 클래스가 먼저 있어야하고 클래스를 기반으로 객체를 만들어서 메소드를 호출해야만한다. C언어처럼 함수의 구현과 호출만으로는 기능을 구현할 수 없다.

그런데 자바8부터는 함수형 프로그래밍 방식인 '람다식'을 지원한다.

 

1. 람다식 표현방법


일반적인 함수의 정의를 람다식으로 바꾸면 다음과 같이 쓸 수 있다.

//C언어에서의 함수 선언
int add(int x, int y) {                            //자바 람다식 표현
   return x+y;                            →       (int x, int y) -> {return x+y};
}

 

람다식은 (매개변수) ->{실행문}; 의 형태로 정의하여 사용한다.

 

 

2. 문법


1) 매개변수 자료형 생략 가능

(int x, int y) -> {return x+y};      →      (x, y) -> {return x+y};

위와 같이 매개변수의 자료형을 생략할 수 있다.

 

2) 매개변수 하나인 경우 괄호 생략 가능

(x) -> {return 2*x};         →       x -> {return 2*x};
(str) -> {System.out.println(str)}    →    str -> {System.out.println};

매개변수가 둘 이상인 경우는 괄호 생략 불가능하다.

 

3) 실행문이 하나인 경우 중괄호 생략 가능

str -> {System.out.println};      →     str -> System.out.println;
<del>x -> {return 2*x};       →     x -> return 2*x;</del> //return문은 중괄호 생략 불가

실행문이 한 문장이면 중괄호가 생략 가능하다. 그러나 return으로 반환값을 반환해야 하는 경우는 안된다.

 

4) 코드 구현부가 return문 한개라면 return 예약어 생략 가능

(x, y) -> {return x+y};    →     (x, y) -> x+y
str -> return str.length();    →     str -> str.length()

코드 구현부가 return문 한개인 경우 return문을 생략하고 식만 쓸 수 있다.

 

3. 람다식 사용


//람다식이 구현할 함수를 정의한 인터페이스 정의
public interface MyNumber {
   int sumCalc(int x, int y);
}

 

public class Hello {

   public static void main(String[] args) {
      MyNumber add = (x,y) -> x+y; //메소드를 구현할 람다식을 인터페이스형 참조변수에 저장
      System.out.println(add.sumCalc(10,20)); //참조변수로 메소드 호출
   }

}

실행결과

30

람다식을 구현하기 위해서는 인터페이스를 만들고 추상메소드를 선언해야 한다. 이후 인터페이스형 참조변수에 추상메소드를 구현할 람다식을 저장하고 참조변수로 메소드를 호출하여 람다식을 사용할 수 있다.

람다식을 구현하기 위해서는 인터페이스 안에 메소드가 단 1개만 있어야한다. 2개이상의 메소드가 있으면 람다식이 구현할 메소드가 무엇인지 모호해진다.

람다식을 구현한 메소드에는 특별히 @FunctionalInterface라는 annotation을 사용할 수 있다. 이 애노테이션은 함수형 인터페이스라는 것을 알려주고 실수로 추가적인 메소드를 선언하면 컴파일 과정에서 오류를 잡아내어준다.

<strong>@FunctionalInterface</strong>
public interface MyNumber { //오류 발생
   int sumCalc(int x, int y);
   int minusCalc(int x, int y);
}

필수는 아니지만 함수형 인터페이스에는 @FunctionalInterface 애노테이션을 사용하여 혹시모를 실수를 방지하는 것이 좋다.

 

 

4. 객체지향 vs 람다식


람다식을 사용하면 객체지향에서 사용되는 코드보다 적은 양의 코드로 같은 기능을 구현할 수 있는 경우가 있다.

다음 인터페이스를 구현하는 경우를 생각해보자.

@FunctionalInterface
public interface MyNumber {
   int sumCalc(int x, int y);
}

 

1) 객체지향으로 구현

//인터페이스 구현 클래스 생성
public class MyNumberImpl implements MyNumber {
   public int sumCalc(int x, int y) {
      return x+y;
   }
}

 

public class TestMyNumber {
   public static void main(String[] args) {
      MyNumberImpl calc = new MyNumberImpl(); //객체 생성
      System.out.println(calc.sumCalc(10, 20));// 메소드 호출
   }
}

 

2) 람다식으로 구현

public class MyNumberLambda {
   public static void main(String[] args) {
      MyNumber add = (x,y)->x+y; //인터페이스형 참조변수에 메소드를 구현할 람다식 저장
      System.out.println(add.sumCalc(10, 20)); //참조변수로 메소드 호출
   }
}

위의 경우는 람다식으로 구현하는 것이 훨씬 간결해진다.

무조건 간결하게 되는 것이 가능한 경우라고해서 람다식을 사용하는 것이 옳은 것은 아니지만 람다식을 적재적소에 잘 사용하면 간결하고 가독성 높은 코드를 작성하는데 도움을 준다.

 

5. 람다식의 비밀


객체지향 프로그래밍인 자바가 객체 생성 없이 메스드를 호출할 수 있었던 것 이유가 무엇일까?

사실 객체 생성 없이 호출했던 것이 아니다. 람다식으로 메소드를 구현하여 호출하면 컴퓨터가 알아서 익명 클래스를 만들고 객체를 생성해서 람다식을 실행시켰던 거다.

//익명 클래스 객체 생성
MyNumber add = new MyNumber(){
   @Override
   public int sumCalc(int x, int y){
      return x+y;
   }
};

 

람다식은 인터페이스형의 참조변수에 저장될 수 있는 걸 알았다. 그렇다면 이를 응용하면 참조변수 자료형으로 매개변수로 전달도 될 것이고 참조변수 자료형의 반환형을 가진 메소드의 반환값으로 사용될 수도 있다.

'프로그래밍 언어 > Java' 카테고리의 다른 글

(Java) 50 - 예외처리  (0) 2020.05.18
(Java) 49 - 스트림  (0) 2020.05.17
(Java) 47 - 내부 클래스  (0) 2020.05.17
(Java) 46 - Map 인터페이스  (0) 2020.05.17
(Java) 45 - Set 인터페이스  (0) 2020.05.17

+ Recent posts