[Java] 람다

최우형·2023년 3월 11일
1

Java

목록 보기
15/24

📌람다

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

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

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

람다 식에서는 기본적으로 반환타입과 이름을 생략할 수 있다.

익명함수(annoymous function)이라고 부르기도 한다.

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

이런 식을

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

이렇게 가능하다.

예시

((((((1))))))

// 기존 방식
void example1() {
	System.out.println(5);
}

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

---
((((((2))))))

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

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

---
((((((3))))))

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

이렇게 표현할 수 있다.

public class LamdaExample1 {
    public static void main(String[] args) {

        // 람다식 Object obj = (num1, num2) -> num1 + num2; 로 대체 가능
        Object obj = new Object() {
            int sum(int num1, int num2) {
                return num1 + num1;
            }
        };

        obj.sum(1, 2);
    }
}

출력 결과
java: cannot find symbol
  symbol:   method sum(int,int)
  location: variable obj of type java.lang.Object

하지만 Ojbect 클래스에는 sum이라는 메서드가 없기 때문에, Object 타입의 참조변수에 담는다고 해도 sum메서드를 사용할 수 없다.

이를 해결하기 위해 함수형 인터페이스(Function Interface)를 사용할 수 있다.

즉, 자바에서 함수형 프로그래밍을 하기 위한 새로운 문법 요소를 도입하는 대신, 기존의 인터페이스 문법을 활용하여 람다식을 다루는 것이라고 할 수 있다.

함수형 인터페이스는 단 하나의 추상 메서드만 선언될 수 있는데, 이는 람다식과 인터페이스의 메서드가 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
public interface MyFunctionalInterface {
    void accept();
}

이 인터페이스를 타겟 타입으로 갖는 람다식은 아래와 같은 형태로 작성해야한다. 람다식에서 매개변수가 없는 이유는 accept()가 매개변수를 가지지 않기 때문이다.

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

// example.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);
}
//예제
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);
}
//예제
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

📌메서드 레퍼런스

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

더욱 더 간편하게 사용 가능

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

left와 right 중 더 큰 수를 리턴하는 식이다.

IntBinaryOperator operato = 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));
  }
}
/*
정적메서드 결과 : 8
인스턴스 메서드 결과 : 15
*/

생성자 참조

(a,b) -> new 클래스(a,b)

클래스 뒤에 ::기호를 붙이고 new 연산자를 기술하면 된다.

클래스::new

생성자가 오버로딩 되어 여러 개가 있을 경우 컴파일러는 함수형 인터페이스의 추상 메서드와 동일한 매개 변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다.

profile
프로젝트, 오류, CS 공부, 코테 등을 꾸준히 기록하는 저만의 기술 블로그입니다!

0개의 댓글