본문 바로가기
Java

람다를 간단하게 써보자

by 완기 2021. 6. 13.
728x90
반응형

자바스크립트 ES6에서 지원하는 Arrow function(화살표 함수) 익명 함수 등, 많은 의미로 불리지만 

 

람다라고 하는 익명 함수를 간단하게 사용해보려고 한다.

자바에서는 8버전 이후, 도입된 함수형 프로그래밍에서 많이 쓰이던 방식이며,

간단하게 풀어 설명하면 간략화된 메서드 선언 및 사용 방식이다.

 

대부분의 메서드는 이름을 가지고 고유의 기능을 수행하는데,

ex) "Hello World!".equals("something");

String 클래스의 equals라는 이름을 가진 메서드처럼 말이다.

 

람다는 이런 이름이 없이 사용하는 함수라 익명 함수라고 불리고, 선언부 및 블록이 없기 때문에 비교적 간단해 보이기는 한다.

 

긴 말 필요 없이 바로 장단점에 대해서 알아보자.

 

장점

1. 코드의 간결성 - 람다를 사용하면 불필요한 반복문의 삭제가 가능하며 복잡한 식을 단순하게 표현할 수 있습니다.

2. 지연 연산 수행 - 람다는 지연 연상을 수행 함으로써 불필요한 연산을 최소화할 수 있습니다.

3. 병렬 처리 가능 - 멀티스레드를 활용하여 병렬 처리를 사용할 수 있습니다.

 

단점

1. 람다식의 호출이 까다롭습니다

2. 람다 stream 사용 시 단순 for문 혹은 while문 사용 시 성능이 떨어집니다.

3. 불필요하게 너무 사용하게 되면 오히려 가독성을 떨어 뜨릴 수 있습니다.

 

다른 사이트를 돌아보면 장단점을 위처럼 많이 설명해두었다.

 

어느 정도 공감이 가지만

오늘 포스팅을 위해 람다식을 작성하기 위해서 느껴본 장단점으로는 몇 가지가 더 있다.

 

1. 별 다른 설명 없이 코드만으로 가독성이 좋다.

2. 선언하기에 따라 다양한 사용이 가능하다.

    ex) a+b를 리턴하는 메서드가 있다면, 람다에서는 선언하기에 따라 같은 함수 하나로 a*b , a-b , a/b 등 정의만 다르게 하면 메서드 하나로 다양하게 사용이 가능.

3. 사용을 위해서 FunctionalInterface를 선언해야 하는데, 클래스가 굉장히 지저분해질 확률이 다분하다.

 

여기서 FunctionalInterface란 인터페이스에 구현해야 할 메서드가 1개만 있는 것을 의미한다.

 

인터페이스 선언해두고 하나만 메서드를 선언해두면 그게 바로 FunctionalInterface다.

 

바로 예제로 들어가 보자.

 

테스트 코드에서 이미지와 같이 선언해주자.

위에 어노테이션은 붙이지 않고, 메서드를 하나만 선언해도 되지만,

 

메서드를 붙이면 인터페이스의 메서드가 1개가 아니라면 런타임 에러를 발생시키므로 실수를 줄이기 위해 붙여주자.

에러가 발생함

 

 

인터페이스는 a, b를 인자로 받는 메서드고 만약 저 메서드를 Implement 하면 자식 클래스에서 직접 구현체를 구현해야 하지만,

람다로 쓰면 어떻게 사용될까?

 

아래는 인터페이스에서 a, b를 인자로 받아 int형이 되는 메서드가 있고, 

위 테스트 코드에선 Operator 인터페이스를 참조하는 변수 example이 메서드를 a+b로 정의하고 있다.

 

참조형 변수의 example.getCalc를 하면 일반적으로 클래스를 인스턴스 화해서 사용하듯 인자를 넘겨주면 위에서 정의된 대로 수를 더한다.

 

 

 

 

 

 

아까 개인적으로 느낀 선언하기에 따라 사용 가능하다고 했던 부분이 어떤 부분이냐면,

 

현재 위 메서드는 간단하게 a+b만 리턴하는 메서드지만 

만약 사칙연산을 위해 더하기 곱하기 빼기 나누기에 대한 메서드를 만든다고 하면,

이미지처럼 메서드를 4개를 작성해야 한다.

하지만 람다식에서 정의만 바꿔주면 저 12줄의 코드가 간결하게 변한다.

 

이미지처럼 메서드가 실행이 되고 새롭게 정의가 됐기 때문에,

람다로 인해 하나의 인터페이스가 다양한 기능을 수행할 수 있다.

결과

만약 인터페이스를 상속받았다면, 직접 구현해야 했기 때문에, 

인터페이스도 메서드가 4개가 선언되고, 상속받은 클래스도 직접 다 구현해하고,

단일 기능밖에 수행하지 못하는 메서드가 탄생한다. (사실은 메서드는 단일 기능 수행이 원칙이긴 하다.)

 

하지만 람다를 이용해 익명 함수로 정의하게 되면 메서드 하나로 각기 다르게 정의하여 다양한 방면으로 활용이 가능하다.

300x250

 

 

그리고 내부 클래스가 더러워진다고 위에서 언급했는데, 

해당 의미는 람다를 호출하기 위해 메서드가 하나만 선언된 인터페이스를 따로 작성해야 하고,

별도의 인터페이스 파일로 존재한다면 너무 파일이 많아지고, 1개의 메서드만 가지고 있기 때문에, 

람다를 사용하는 개수만큼 인터페이스가 선언된다. (위처럼 4가지를 활용한다면 줄 수 있겠지만...)

 

그렇기 때문에 테스트 코드처럼 내부 인터페이스를 선언해서 바로 사용하게 되는 경우가 많을 텐데, 

한 클래스 파일이 너무 더러워질 가능성이 다분해진다.

 

 

그리고 하나 예제를 더 해보자면, 

회원가입에서 사용할 수 있을 것 같은 람다 예제를 준비했다.

 

 public class Member {

      String name;
      int age;
      String dept;

      @Override
      public String toString() {
         return "Member{" +
             "name='" + name + '\'' +
             ", age=" + age +
             ", dept='" + dept + '\'' +
             '}';
      }
      public Member(String name, int age, String dept) {
         this.name = name;
         this.age = age;
         this.dept = dept;
      }
   }

회원 정보 매핑을 위한 간단한 클래스를 만들어주고,

 

@FunctionalInterface
   public interface LambdaTest {
      Member setMember(String name, int age, String dept);
   }

인터페이스는 위처럼 작성했다.

인자를 다 넣고 메서드는 Member 객체를 리턴한다.

 

@Test
   public void contextLoads() {
      LambdaTest setObj = (String name, int age, String dept) -> new Member(name, age, dept);
      System.out.println(setObj.setMember("Jeon Wan Gi", 27, "Developer").toString());
   }

인터페이스 LambdaTest를 setObj가 참조하고, 받은 인자들을 통해 Member 객체를 리턴하도록 설정한 후,

사용자가 입력한 파라미터들을 넣어주면

 

setObj.setMember("Jeon Wan Gi", 27, "Developer")

이 부분이 결국 new Member("Jeon Wan Gi",27 , "dev")처럼 되는 것이다.

 

 

Member에 값이 잘 매핑된 모습이다.

저 객체를 바로 JPA나 Mybatis에 바로 넘겨버리면 회원 가입이 순식간에 완성되는 셈이다.

 

트랜잭션 처리가 많거나 오래 걸리는 부분에서 람다를 잘 활용해서 성능을 높이는데 잘 활용해봐야겠다.

728x90
728x90

댓글