람다식

이용만·2023년 3월 8일
0

🔎람다식 : 함수형 프로그래밍 기법을 지원하는 자바 문법 요소

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

✒️람다식의 기본 문법

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

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

람다식에서는 기본적으로 반환타입과 이름을 생략할 수 있다.
그래서 람다함수를 종종 이름이 없는 함수, 익명 함수라 부르기도 한다.

👉메서드를 람다식으로 만드는 과정

// 기존 방식
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
}

//더 축약기 가능하다. 메서드 바디에 문장이 실행문이 하나만 존재할 떄 중괄호와 return문을 생량 할 수 있다.
(int num1, int num2) -> num1 + num2

//또한 매개변수 타입을 함수형 인터페이스를 통해 유추할 수 있는 경우 매개변수 타입을 생략할 수 있다.
(num1, num2) -> num1 + num2

문제는 람다식은 익명 객체이기 때문에 객체를 담아줄 Object 클래스 타입의 참조변수를 선언 후
람다식을 obj에 할당하면 되지만 obj에는 max 기능이 없기에 문제가 나타난다.
(->익명 클래스 생성, 객체 생성 동시에 진행)

그래서 함수형 인터페이스를 사용한다.
위 그림을 람다식으로 표현하면

(a,b) -> a > b ? a : b; 

🔎함수형 인터페이스

자바에서 함수는 반드시 클래스 안에 정의되어야 하기에 메서드가 독립적으로 있을 수 없고
반드시 클래스 객체를 먼저 생성한 후 생성한 객체로 메서드를 호출한다.
이처럼 람다식 또한 원래 객체이다. 이름이 없기 때문에 익명 객체라 표현한다.
함수형 인터페이스는 기존의 인터페이스 문법을 활용하여 람다식을 다루는 것
함수형 인터페이스는 단 하나의 추상 메서드만 선언될 수 있는데, 이유는 람다식과 인터페이스의 메서드가 1:1로 매칭 되어야 하기 때문이다.

ExampleFunction에 sum() 추상메서드가 정의되어 있다.
함수형 인터페이스(ExampleFunction)는 람다식을 참조할 참조변수를 선언할 때 타입으로 사용하기 위해 필요하다.
함수형 인터페이스 타입으로 선언된 참조변수 exampleFunction에 람다식이 할당되었고,
exampleFunction을 통해 Sum()메서드를 호출한다.
함수형 인터페이스를 사용해서 참조변수 타입으로 함수형 인터페이스를 사용하여 원하는 메서드에 접근이 가능한 것이다.


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

@FunctionalInterface
interface MyFunctionalInterface {
    void accept();
}
public class MyFunctionalInterfaceExample {
		public static void main(String[] args) throws Exception {
				MyFunctionalInterface example = () -> System.out.println("accept() 호출");
				example.accept();
		}
}
// 출력값
accept() 호출

👉매개변수가 있는 람다식

@FunctionalInterface
public interface MyFunctionalInterface {
    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

👉리턴값이 있는 람다식

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문만 있을 경우, 중괄호 {}와 return문 생략가능
        int result3 = example.accept(2, 5);
        System.out.println(result3);
       

        example = (x, y) -> sum(x, y);
				//return문만 있을 경우, 중괄호 {}와 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

🧐함수형 인터페이스를 사용하는 이유?

이유는 자바의 람다식은 함수형 인터페이스로만 접근이 가능하기 때문이다.

참고
https://codechacha.com/ko/java8-functional-interface/#1-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0

함수형 인터페이스 패키지 모음


🔎메서드 참조

람다식에서 불필요한 매개변수를 제거할 때 주로 사용한다.

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

정적 메서드를 참조할 경우에는 클래스 이름 뒤에 "::" 기호를 붙이고 정적 메서드 이름을 기술한다.

클래스 :: 메서드

인스턴스 메서드는 먼저 객체를 생성하고 참조 변수 뒤에 :: 기호를 붙이고 인스턴스 메서드 이름을 기술하면 된다.

👉예제코드를 보자


출력
정적메서드 결과 : 8
인스턴스메서드 결과 : 15

ApplyAsInt() 메서드는 매개값으로 두 개의 int를 가지는 메서드이다.

✒️생성자

메서드 참조는 생성자 참조도 포함한다.
생성자를 참조한다는 것은 객체 생성을 의미한다.

(a,b) -> 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;
  }
}
public class ConstructorRef {
  public static void main(String[] args) throws Exception {
    Function<String, Member> function1 = Member::new; -> (s) -> new Member(s);
    Member member1 = function1.apply("kimcoding");

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

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

위의 코드 예제는 생성자 참조를 이용해서 두 가지 방법으로 Member 객체를 생성하고 있습니다.

하나는 Function<String, Member> 함수형 인터페이스의 Member apply(String) 메서드를 이용해서 Member 객체를 생성하고, 다른 하나는 BiFunction<String, String, Member> 함수형 인터페이스의 Member 객체를 생성합니다.

이때 생성자 참조는 두 가지 방법 모두 동일하지만, 실행되는 Member 생성자가 다른 것을 볼 수 있습니다.

-> 많이 쓰이는 배열과 메서드 참조

Function<Integer, int[]> f = (x) -> new int[x];
Function<Integer, int[]> f2 = int[]::new;
profile
성장하는 개발자가 되고자 합니다.

0개의 댓글