[java] 람다

이동엽·2023년 1월 8일
0

람다식이란 무엇인가?

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

  • 메소드를 하나의 식으로 표현한 것으로, 코드를 매우 간결하며 명확하게 표현할 수 있다
  • 자바에서 함수는 반드시 클래스 안에 정의 되어야하고, 메소드는 독립적으로 있을 수 없다. 무조건 클래스 객체를 먼저 생성하고, 생성한 객체로 메소드를 호출해야한다.
  • 그래서 람다식은 객체이다. 이름이 없기때문에 익명 객체라고 볼수 있다.
  • 익명 클래스 통해 만들 수 있는데 객체의 선언과 생성을 동시에 하고, 객체 하나만 생성하고 딱 한번만 사용되는 일회용 클래스다.
//기존 메소드 표현 방식
void sayHello(){
	System.out.println("Hello")
}
//위의 코드를 람다식으로 표현한 식
() -> System.out.println("Hello")

이렇게 반환타입이나 이름을 생략할수 있다. 따라서 익명 함수라고 부르기도 한다.

//sum 메소드
int sum(int num1, int num2){
	return num1+num2;
}
//이걸 이렇게 반환타입,메소드 명 제거,화살표추가
(int num1, int num2) ->{
	return num1+num2;
}
//더 축약하면 메소드 바디에 문장이 실행문 하나만 존재할 때 우리는 중괄호,return 문을 생략할수 있다, 세미콜론도 생략해야한다.
(int num1, int num2) ->num1+ num2
//매개변수 타입을 함수형 인터페이스를 통해 유추할수 있다면 매개변수 타입도 생략할수 있다.
(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 {
		int sum(int num1, int num2);
}

// 출력값
25

저 인터페이스는 람다식을 참조할 참조변수를 선언할 때, 타입으로 사용하기 위해 필요하다.
@FunctionalInterface 이건 컴파일러가 인터페이스를 확실히 식별시키기 위한 애너테이션이다.

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

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

MyFunctionalInterface example = ()->{~~~}
example.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

매개변수가 있고 리턴값이 없는 인터페이스의 타입 람다식은 이런 형태이다.

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

@FunctionalInterface
public interface MyFunctionalInterface {
    int accept(int x, int y);
}
//리턴타입이 있기때문에 return문이 있어야하며,특정 경우엔 중괄호,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

메소드 레퍼런스(메소드 참조)

  • 불필요한 매개변수를 제거할때 사용, (람다식을 더 간단하게 사용)
//Math 클래스의 max() 메소드를 호출하는 람다식
(left,right) -> Math.max(left,rigth)
이걸 메소드 참조를 이용하면
//클래스이름::메소드이름
Math :: max 
  • 메소드 참조도 인터페이스의 추상 메소드가 어떤 매개 변수를 가지고, 리턴 타입이 무엇인가에 따라 달라진다.

정적 메소드,인스턴스 메소드 참조

정적메소드를 참조할 경우

클래스::메소드

클래스 이름 뒤에 ::기호 붙이고 정적 메소드 이름을 기술한다.

인스턴스 메소드를 참조할 경우

참조 변수 ::메소드

객체를 생성한 다음 참조변수 뒤에 ::기호 붙이고 인스턴스 메소드 이름을 기술한다.

예제로 비교해보면

//IntBinaryOperator 인터페이스는 두 개의 int 매개값을 받아 int 값을 리턴하므로, int 형식의 메서드 참조를 대입할 수 있습니다.
//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
*/

생성자 참조

클래스 이름 뒤에 ::기호를 붙이고 new 연산자를 기술한다.
만약 생성자가 여러개 있을경우 컴파일러가 함수형 인터페이스의 추상 메소드와 동일한 매개변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다.

(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;
  }
}
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) 실행
*/

생성자 참조를 이용해 두가지 방법으로 Menber 객체를 생성하고 있다.
1. Function<String,Member> 함수형 인터페이스의 Member apply(String)메소드를 이용해 Member 객체를 생성한다.
2. BiFunction<String String,Member>함수형 인터페이스의 Member 객체를 생성한다.

profile
씨앗

0개의 댓글