람다(Lambda)

귀찮Lee·2022년 5월 22일
0

Java

목록 보기
10/15

◎ 람다(Lambda)

  • 배경

    • 객체 지향 프로그래밍 이전에도 함수형 프로그래밍 언어들이 있었는데, 현업에서 큰 호응을 받지 못함
    • 최근 들어 병렬 처리와 이벤트 지향 프로그래밍에 적합하다는 이유로 함수형 프로그래밍이 다시 각광받음
    • 객체 지향 프로그래밍과 함수형 프로그래밍을 혼합함으로써 더욱 효율적인 프로그래밍을 할 수 있도록 프로그래밍 패러다임이 변화
  • 람다식(Lambda Expressions)

    • 익명 함수를 생성하기 위한 식 (객체지향보다는 함수 지향 언어에 가까움)
    • 장점 : 자바 코드가 매우 간결해지고, 컬렉션의 요소를 필터링하거나 매핑해서 원하는 결과를 쉽게 집계 가능
    • 형태 : 매개변수를 가진 코드 블록이지만, 런타임에 익명 구현 객체를 생성
    interface InterfaceExample {
        int CONSTANT = 3;
        default void printTest() {};
    }
    
    // 익명 내부 클래스
    InterfaceExample interfaceExample = new InterfaceExample() {
      public void printTest() {...}
    }
    
    // 람다식 : (매개변수) -> {실행코드} 형태
    // InterfaceExample의 익병 구현 객체 생성
    InterfaceExample interfaceExample = () -> {...}

◎ 람다식의 기본 문법

// 1. 기본 작성
	(타입 매개변수) -> { ... }

// 2. 매개변수가 1개 일 때, 매개변수 () 생략가능
	매개변수 -> { ... }

// 3. 매개변수가 2개 이상이고, 리턴문만 존재할 때는 return을 생략가능
	(매개변수1, 매개변수2) -> 리턴값;
	(num1, num2) -> {return num1 + num2} // return문만 존재하므로
	(num1, num2) -> num1 + num2 // return문만 있을 경우, return 생략가능, 중괄호도 생략

// 4. 매개변수가 2개 이상이고, 실행문을 실행하고 결과값을 리턴할 경우
	(매개변수1, 매개변수2) -> { ... };

◎ 함수형 인터페이스

  • 함수형 인터페이스
    • 정의 : 추상 메서드가 단 한개인 인터페이스
    • 람다식을 이용하기 위해, 인터페이스의 문법을 활용해 람다식을 표현
    • 람다식을 사용하면, 메서드 한개만 구현할 수 있으므로, 추상 메서드를 한개만 가지고 있어야 한다.
    • @FunctionalInterface : 함수형 인터페이스 구현시, 필수적으로 사용하지 않아도 되나, 추상 메서드가 2개 이상일 경우 에러를 내기 때문에 쉽게 확인할 수 있음.
    @FunctionalInterface // 두 개 이상의 추상 메서드가 선언되면 컴파일 오류를 발생
    InterfaceExample interfaceExample = new InterfaceExample() {
        public void printTest() {...}
      }

◎ Java 에서 기본적으로 제공하는 Functional Interfaces

  • 매번 함수형 인터페이스를 만드는 것은 번거로우므로, Java에서 기본적으로 많이 사용되는 함수형 인터페이스를 제공
  • 기본적으로 제공되는 것만 사용해도 웬만한 람다식은 다 만들 수 있기 때문에 개발자가 직접 함수형 인터페이스를 만드는 경우는 거의 없다.

◎ 람다식 사용예시

public class LambdaExample {
    public static void main(String[] args) {
        Printsometh printsometh;

        printsometh = () -> System.out.println("sout first");
        printsometh.print(); // sout first

        printsometh = () -> {
            String string = "sout second";
            System.out.println(string);
        };
        printsometh.print(); // sout second

        Multiply multiply;

        multiply = (num1, num2) -> num1*num2;
        System.out.println(multiply.cal(3,4)); // 12

        multiply = ((num1, num2) -> {return num1*num2;});
        System.out.println(multiply.cal(3,4)); // 12

    }
}

@FunctionalInterface
interface Printsometh{
    void print();
}

@FunctionalInterface
interface Multiply{
    int cal(int num1, int num2);
}

◎ 메서드 레퍼런스

  • 목적 : 불필요한 매개변수를 제거하여 람다식 표현

    IntBinaryOperator operato = (left, right) -> Math.max(left, right);
    //예시 : 단순히 두 개의 값을 Math.max() 메서드의 매개값으로 전달하는 역할만 하기 때문에 다소 불편힘
    
    // 메서드 레퍼런스 사용
    IntBinaryOperator operato = Math :: max;
  • 메서드 레퍼런스 종류

    • 정적 메서드를 레퍼런스 : " 클래스 :: 매서드 " 형식
    • 인스턴스 메서드를 레퍼런스 : " 참조 변수 :: 메서드 " 형식
    class Calculator {
      static int staticMethod(int x, int y) {
        return x + y;
      }
    
      int instanceMethod(int x, int y) {
        return x * y;
      }
    }
    
    public class MethodReferences {
      public static void main(String[] args) throws Exception {
        IntBinaryOperator operator;
    
        //static method
        operator = Calculator::staticMethod; // 클래스 이름을 이용
        System.out.println("정적메서드 결과 : " + operator.applyAsInt(3, 5));
        // 정적메서드 결과 : 8
    
        //instance method
        Calculator calculator = new Calculator();
        operator = calculator::instanceMethod; // 만든 인스턴스의 참조 변수를 이용
        System.out.println("인스턴스 메서드 결과 : "+ operator.applyAsInt(3, 5));
        // 인스턴스 메서드 결과 : 15
        
        // operator = calculator::staticMethod;
        // 정적 메세드를 참조 변수를 이용해 레퍼런스를 하면 에러!
      }
    }
    • 매개변수의 메서드 레퍼런스
      • 람다식에서 제공되는 a 매개변수의 메서드를 호출해서 b 매개변수를 매개값으로 사용하는 경우에도 레퍼런스로 표현가능
      • 작성방법은 정적 메서드 레퍼런스와 동일하지만 a의 인스턴스 메서드가 참조되므로 전혀 다른 코드가 실행
    public class ParameterMethodReferenceExample {
        public static void main(String[] args) {
            Equal equal = ((str1, str2) -> str1.equals(str2));
            System.out.println(equal.equal("1","1")); // true
    
            equal = String::equals;
            System.out.println(equal.equal("1","1")); // true
    
        }
    }
    
    @FunctionalInterface
    interface Equal{
        boolean equal(String str1, String str2);
    }
    • 생성자 참조 (메서드 레퍼런스는 생성자 참조도 포함)
      • 단순히 객체를 생성하고 리턴하도록 구성된 람다식은 생성자 참조로 대치 가능
    public class ConstructorRef {
      public static void main(String[] args) throws Exception {
      
        Function<String, Member> function1 = Member::new;
        Member member1 = function1.apply("kimcoding");
        // Member(String id) 실행
    
        BiFunction<String, String, Member> function2 = Member::new;
        Member member2 = function2.apply("kimcoding", "김코딩");
        // Member(String name, String id) 실행
      }
    }
    
    public class 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;
      }
    }
profile
배운 것은 기록하자! / 오류 지적은 언제나 환영!

0개의 댓글