람다

seongmin·2022년 9월 17일
1

Java

목록 보기
10/30
post-thumbnail

람다

  • 람다식(Lambda Expression)은 함수형 프로그래밍 기법을 지원하는 자바의 문법요소

  • 람다식은 메서드를 하나의 ‘식(expression)’으로 표현한 것으로, 코드를 매우 간결하면서 명확하게 표현할 수 있다는 큰 장점이 있다.

//기존 메서드 표현 방식
void sayhello() {
	System.out.println("HELLO!")
}

//위의 코드를 람다식으로 표현한 식
() -> System.out.println("HELLO!")

기본적으로 반환타입과 이름을 생략할 수 있다. 따라서 람다함수를 종종 이름이 없는 함수, 즉 익명 함수(anonymous function)라 부르기도 한다.

int sum(int num1, int num2) {
	return num1 + num2;
}

(int num1, int num2) -> { // 반환타입과 메서드명 제거 + 화살표 추가
	return num1 + num2;
}

반환타입과 메서드명을 제거하고 코드 블럭사이에 화살표를 추가

(int num1, int num2) -> {  // 또한 반환값이 있는 메서드의 경우에는 
                           // return문과 문장 뒤에 오는 세미콜론(;)을 생략할 수 있다.
	num1 + num2
}  
  • 메서드 바디에 문장이 실행문이 하나만 존재할 때 우리는 중괄호를 생략할 수 있다.
(int num1, int num2) -> num1 + num2
  • 매개변수 타입을 쉽게 유추할 수 있는 경우에는 매개변수의 타입을 생략할 수 있다.
(num1, num2) -> num1 + num2

함수형 인터페이스

// sum 메서드 람다식
(num1, num2) -> num1 + num2

// 람다식을 객체로 표현
new Object() {
	int sum(int num1, int num2) {
		return num1 + num1;
	}
}
  • sum 메서드는 사실 아래와 같은 익명 클래스. 익명 클래스란 객체의 선언과 생성을 동시에 하여 오직 하나의 객체를 생성하고, 단 한번만 사용되는 일회용 클래스이다.
new Object() {   //익명 클래스
	int sum(int num1, int num2) {
		return num1 + num1;
	}
}
  • 람다식이 객체라 한다면 이 객체에 접근하고 사용하기 위한 참조변수가 필요하다.그런데 기존에 객체를 생성할 때 만들었던 Object 클래스에는 sum 이라는 메서드가 없기 때문에, Object 타입의 참조변수에 담는다고 하더라도 sum 메서드를 사용할 수 없다. 이 같은 문제를 해결하기 위해 사용하는 자바의 문법 요소가 바로 자바의 함수형 인터페이스(Functional Interface)라 할 수 있다.

  • 기존의 인터페이스 문법을 활용하여 람다식을 다루는 것인데, 이것이 가능한 이유는 람다식도 결국 하나의 객체이기 때문에 인터페이스에 정의된 추상메서드를 구현할 수 있기 때문이다.

  • 함수형 인터페이스에는 단 하나의 추상 메서드만 선언될 수 있는 데, 람다식과 인터페이스의 메서드가 1:1로 매칭되어야 하기 때문이다.

매개변수와 리턴값이 없는 람다식

@FunctionalInterface
public interface MyFunctionalInterface {
    public void accept();
}
  • 람다식에서 매개변수가 없는 이유는 accept()가 매개변수를 가지지 않기 때문이다.
MyFunctionalInterface example = () -> { ... };

// example.accept();
  • 람다식이 대입된 인터페이스의 참조 변수는 위의 주석과 같이 accept() 를 호출할 수 있다. accept() 의 호출은 람다식의 중괄호 {} 를 실행시킨다.

매개변수가 있는 람다식

  • 람다식의 배개변수가 하나인 이유는 accept() 의 매개변수가 하나이기 때문이다.
@FunctionalInterface
public interface MyFunctionalInterface {
    public void accept(int x);
}

리턴값이 있는 람다식

  • accept() 가 리턴 타입이 있기 때문에 중괄호 { } 에는 return 문이 있어야 한다.
@FunctionalInterface
public interface MyFunctionalInterface {
    public int accept(int x, int y);
}

메서드 레퍼런스

  • 메서드 참조는 람다식에서 불필요한 매개변수를 제거할 때 주로 사용합니다.
(left, right) -> Math.max(left, right);

// 아래의 형태로 깔끔하게 처리 가능 
// 클래스이름::메서드이름

Math :: max; // 메서드 참조

정적 메서드와 인스턴스 메서드 참조

  • 정적 메서드를 참조할 경우에는 클래스 이름 뒤에 :: 기호를 붙이고 정적 메서드 이름을 기술하면 된다.
클래스 :: 메서드
  • 인스턴스 메서드의 경우에는 먼저 객체를 생성한 다음 참조 변수 뒤에 :: 기호를 붙이고 인스턴스 메서드 이름을 기술하면 된다.
참조 변수 :: 메서드
//Calculator.java
public class Calculator {
  public static int staticMethod(int x, int y) {
                        return x + y;
  }

  public int instanceMethod(int x, int y) {
   return x * y;
  }
}
import java.util.function.IntBinaryOperator;

public class MethodReferences {
  public static void main(String[] args) throws Exception {
    IntBinaryOperator operator;

    /*정적 메서드
		클래스이름::메서드이름
		*/
    operator = Calculator::staticMethod;
    System.out.println("정적메서드 결과 : " + operator.applyAsInt(3, 5));

    /*인스턴스 메서드
		인스턴스명::메서드명
		*/
		
    Calculator calculator = new Calculator();
    operator = calculator::instanceMethod;
    System.out.println("인스턴스 메서드 결과 : "+ operator.applyAsInt(3, 5));
  }
}

생성자 참조

  • 생성자를 참조한다는 것은 객체 생성을 의미한다. 단순히 메서드 호출로 구성된 람다식을 메서드 참조로 대치할 수 있듯이, 단순히 객체를 생성하고 리턴하도록 구성된 람다식은 생성자 참조로 대치 가능하다.
(a,b) -> {return new 클래스(a,b);};

//생성자 참조 문법

클래스 :: new
  • 생성자가 오버로딩 되어 여러 개가 있을 경우 컴파일러는 함수형 인터페이스의 추상 메서드와 동일한 매개 변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다.
public class ConstructorRef {
  public static void main(String[] args) throws Exception { // Member 클래스 
    Function<String, Member> function1 = Member::new;
    Member member1 = function1.apply("kimcoding");

    BiFunction<String, String, Member> function2 = Member::new;
    Member member2 = function2.apply("kimcoding", "김코딩");
  }
}

0개의 댓글