람다(Lambda)

Nak Jun Choi·2022년 11월 14일

람다란?

  • 함수형 프로그래밍 기법을 지원하는 자바의 문법요소이다.

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

  • 익명의 객체이다. 때문에 기존 자바의 문법 요소를 해치지 않으면서 함수형 프로그래밍 기법을 사용할 수 있는 장치가 필요했다. 이에 따라 함수형 인터페이스(Functional Interface)가 만들어지게 되었다.

람다식의 기본 문법

  • 람다식에는 기본적으로 반환타입과 이름을 생략할 수 있다. 따라서 람다함수를 익명 함수(Anonymous Function)라 부르기도 한다.
// 기존 방식
void example1() {
	System.out.println(5);
}

// 람다식
() -> {System.out.println(5);}


// 기존 방식
int example2() {
	return 10;
}

// 람다식
() -> {return 10;}

// 기존 방식
void example3(String str) {
	System.out.println(str);
}

// 람다식
(String str) -> {	System.out.println(str);}
  • 특정 조건이 충족되면 람다식을 더욱 충약하여 표현할 수 있다.
// 기존 방식
int sum(int num1, int num2) {
	return num1 + num2;
}

// 람다식
(int num1, int num2) -> {
	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;
	}
}
  • 익명 클래스란 객체의 선언과 생성을 동시에 하여 오직 하나의 객체를 생성하고, 단 한번만 사용되는 일회용 클래스이다.
  • 아래 코드를 보면, Object클래스에는 sum이라는 메서드가 없기때문에, object타입의 참조변수에 담아도 sum메서드를 사용할 수 없다. 이 문제를 해결하기 위해 '함수형 인터페이스'를 사용한다.
public class LamdaExample1 {
    public static void main(String[] args) {
		   Object obj = new Object() {
            int sum(int num1, int num2) {
                return num1 + num1;
            }
        };
			// 람다식 Object obj = (num1, num2) -> num1 + num2 로 대체 가능
}
  • 람다식도 하나의 객체이기 때문에 인터페이스에 정의된 추상메서드를 구현할 수 있기 때문이다.
  • 함수형 인터페이스에는 단 하나의 추상 메서드만 선언될 수 있다. 람다식과 인터페이스의 메서드가 1:1로 매칭되어야 하기 때문이다.
public class LamdaExample1 {
    public static void main(String[] args) {
		   /* Object obj = new Object() {
            int sum(int num1, int num2) {
                return num1 + num1;
            }
        };
			*/ 
		ExampleFunction exampleFunction = (num1, num2) -> num1 + num2;
		System.out.println(exampleFunction.sum(10,15));
}

@FunctionalInterface // 컴파일러가 인터페이스가 바르게 정의되었는 지 확인할 수 있도록
interface ExampleFunction {
		public abstract int sum(int num1, int num2);
}

// 출력값
25

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

@FunctionalInterface
public interface MyFunctionalInterface {
    public void accept();
}


public class MyFunctionalInterfaceExample {
	public static void main(String[] args) throws Exception {
		MyFunctionalInterface example;
		example = () -> {
			String str = "첫 번째 메서드 호출!";
			System.out.println(str);
		};
		example.accept();

		example = () -> System.out.println("두 번째 메서드 호출!");
		//실행문이 하나라면 중괄호 { }는 생략 가능
		example.accept();
	}
}

// 출력값
첫 번째 메서드 호출!
두 번째 메서드 호출!

매개변수가 있는 람다식

@FunctionalInterface
public interface MyFunctionalInterface {
    public void accept(int x);
}

public class MyFunctionalInterfaceExample {
    public static void main(String[] args) throws Exception {
        MyFunctionalInterface example;
        example = (x) -> {
            int result = x * 5;
            System.out.println(result);
        };
        example.accept(2);

        example = (x) -> System.out.println(x * 5);
        example.accept(2);
    }
}

// 출력값
10
10

리턴값이 있는 람다식

@FunctionalInterface
public interface MyFunctionalInterface {
    public int accept(int x, int y);
}

public class MyFunctionalInterfaceExample {
    public static void main(String[] args) throws Exception {
        MyFunctionalInterface example;
        example = (x, y) -> {
            int result = x + y;
            return result;
        };
        int result1 = example.accept(2, 5);
        System.out.println(result1);
        

        example = (x, y) -> { return x + y; };
        int result2 = example.accept(2, 5);
        System.out.println(result2);
       

	      example = (x, y) ->  x + y;
				// 실행문이 한 줄인 경우, 중괄호 {}와 return문 생략가능
        int result3 = example.accept(2, 5);
        System.out.println(result3);
       

        example = (x, y) -> sum(x, y);
				// 실행문이 한 줄인 경우, 중괄호 {}와 return문 생략가능
        int result4 = example.accept(2, 5);
        System.out.println(result4);
 
    }

    public static int sum(int x, int y){
        return x + y;
    }
}

//출력값
7
7
7
7

자바에서 기본적으로 제공하는 함수형 메서드

메서드 레퍼런스

  • 메서드 참조는 람다식에서 불필요한 매개변수를 제거할 때 주로 사용한다.

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

  • 정적 메서드
    • 클래스 :: 메서드
  • 인스턴스 메서드
    • 참조 변수 :: 메서드
//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));
  }
}
/*
정적메서드 결과 : 8
인스턴스 메서드 결과 : 15
*/

생성자 참조

(a,b) -> {return new 클래스(a,b);};

//생성자 참조 문법

클래스 :: new
  • 생성자가 오버로딩 되어 여러 개가 있을 경우 컴파일러는 함수형 인터페이스의 추상 메서드와 동일한 매개 변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다.
//Member.java
public class Member {
  private String name;
  private String id;

  public Member() {
    System.out.println("Member() 실행");
  }

  public Member(String id) {
    System.out.println("Member(String id) 실행");
    this.id = id;
  }

  public Member(String name, String id) {
    System.out.println("Member(String name, String id) 실행");
    this.id = id;
    this.name = name;
  }

  public String getName() {
    return name;
  }

public String getId() {
    return id;
  }
}
import java.util.function.BiFunction;
import java.util.function.Function;

public class ConstructorRef {
  public static void main(String[] args) throws Exception {
    Function<String, Member> function1 = Member::new;
    Member member1 = function1.apply("kimcoding");

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

/*
Member(String id) 실행
Member(String name, String id) 실행
*/
profile
HelloWorld

0개의 댓글