람다

이정연·2023년 1월 6일

자바기초

목록 보기
2/25

람다식

  • 함수형 프로그래밍 기법을 지원하는 자바의 문법요소임
  • 메서드를 하나의 식 으로 표현한 것
  • 코드를 매우 간결하면서 명확하게 표현할 수 있다.
//기존 메서드 표현 방식
void sayhello() {
	System.out.println("HELLO!")
}

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

기본적으로 반환타입과 이름을 생략할 수 있음. 따라서 람다함수를 익명함수(anonymous function)라고도 함.

ex

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

// 람다식
() -> {return 10;}
======================
// 기존 방식
void example3(String str) {
	System.out.println(str);
}

// 람다식
(String str) -> {System.out.println(str);}
  • 실행문이 2개 이상인 경우에는 중괄호를 생략할 수 없음.
  • 실행문이 하나만 존재할대 중괄호와 return문을 생략할 수 있음. 이경우 세미콜론까지 생략함.
// 기존 방식
int sum(int num1, int num2) {
	return num1 + num2;
}

//람다식
(int num1, int num2) -> num1 + num2

함수형 인터페이스

  • 자바에서 함수는 반드시 클래스 안에서 정의되어야 하기 때문에 메서드가 독립적으로 있을 수 없음
  • 따라서 꼭 클래스 객체를 먼저 생성한 후 생성한 객체로 메서드를 호출해야 함.
  • 람다식은 메서드 같지만 사실은 객체임. 이름이 없기 때문에 익명의 객체
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 {
		int sum(int num1, int num2);
}
  • 함수형 인터페이스인 ExampleFunction에 추상메서드 sum() 이정의되어있다.
  • 이 함수형 인터페이스는 람다식을 참조할 참조변수를 선언할때 타입으로 사용하기 위함.

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

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

이 인터페이스를 타겟 타입을 갖는 람다식은 다음과 같아야 함.

MyFunctionalInterface example = () -> { ... };

// example.accept();

주석과 같이 accept 호출 할 수 있다.

  • 예시
@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);  //void 는 rerurn값을 가지지 않기때문에 아래 에서 return값을 지정하지않고 println으로 나타냄
}

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);  //accept에 대해 정의함.
        example.accept(2); //accept메서드를 사용함.
    }
}

// 출력값
10
10

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

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

이 인터페이스를 타겟 타입으로 갖는 람다식은 매개변수가 두개이로 작성해야하며, accept()가 리턴타입이 있기때문에
중괄호 {}에는 return문이 있어야 합니다.

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

메서드 레퍼런스

  • 자바에서 함수는 반드시 클래스 안에서 정의되어야 하기 때문에 메서드가 독립적으로 있을 수 없고 클래스 객체를 먼저 생성한 후 생성한 객체로 메서드를 호출해야 합니다.
  • 위에서 MyFunctionalInterface example; 이 클래스 객체를 생성하는것
example = (x, y) ->  x + y;
				//return문만 있을 경우, 중괄호 {}와 return문 생략가능
        int result3 = example.accept(2, 5);
        System.out.println(result3);

생성한 객체로 메서드 호출하는 것

다음은 두개의 값을 받아 큰 수를 리턴하는 Math클래스의 max()정적 메서드를 호출하는 람다식임.

(left, right) -> Math.max(left, right)

//메서드 참조를 이용하면 깔끔하게 처리가능
Math :: max //메서드 참조

메서드 참조도 람다식과 마찬가지로 익명 구현 객체로 생성되므로, 인터페이스의 추상메서드가 어떤 매개 변수를 가지고, 리턴타입이 무엇인지에 따라 달라진다.

IntBinaryOperator 인터페이스는 두 개의 int 매개값을 받아 int 값을 리턴하므로, Math::max 메서드 참조를 대입할 수 있습니다.

IntBinaryOperator operato = Math :: max; //메서드 참조

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

예제

public class Calculator {
  public static int staticMethod(int x, int y) {
                        return x + y;
  }

  public int instanceMethod(int x, int y) {
   return x * y;
  }
}

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
*/

생성자 참조

  • 생성자를 참조한다는 것은 객체 생성을 의미한다.
  • 생성자가 오버로딩(같은이름을 갖는 여러개의 생성자) 되어 여러개인경우 함수형 인터페이스의 추상메서드와 매개 변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다.
public class Member {
  private String name;
  private String id;
  
  public Member(String id) { //----------------------(1)
    System.out.println("Member(String id) 실행");
    this.id = id;
  }

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

위와 같이 정의 되어있을때 Member로 두개의 생성자가 정의되어있는것을 볼 수있다. 즉, 오버로딩 되어있다.이때 생성자를 참조할때 매개변수의 타입과 개수를 따져서 적절한 생성자를 찾아 실행한다.

public class ConstructorRef {
  public static void main(String[] args) throws Exception {
  
    Function<String, Member> function1 = Member::new; //Member생성자를 참조할거야 선언하고
    Member member1 = function1.apply("kimcoding");    //근데 Member가 여러개네? 그러면 앞에 설정한 조건에 맞는것을 찾아 쓸거야

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

위의 코드는 생성자 참조를 이용해서 Member 객체를 생성하고 있는데, Function과 BiFunction을 이용하고 있다. 이 두개의 함수형 인터페이스 내부의 apply() 메서드를 이용해서 사용하고있다.
Function 은 (1)을 가져다가 쓰고, BiFunction은 (2)를 가져다 쓴다. 왜? 매개변수의 갯수로 판별하여 알아서 가져와서 쓴다.

근데!

Member member3 = new Member("kim","4");

이거를해도 같은결과인데 굳이 Function 인터페이스의 apply를 이용해가면서 하는이유가 뭐지?

profile
반갑습니다.

0개의 댓글