본문 바로가기

diary/codestates (be39)

05/19-20/22 람다식 Lambda

람다식

익명 함수를 생성하기 위한 식

함수 지향 언어에 가까움 (opp보다)

코드가 간결해지고, 컬렉션의 요소를 필터링/매핑해서 원하는 결과를 쉽게 얻을 수 있기 때문에 사용한다

** 매개 변수를 가진 코드 블록의 형태이지만, 실행 시 익명 구현 객체를 생성하게 된다.

 

람다식 → 매개 변수를 가진 코드 블록 → (런타임) 익명 구현 객체

 

익명 구현 객체란? → 이름이 없음

 

Runnable 인터페이스의 익명 구현 객체를 생성하는 방법

Runnable runnable = new Runnable(){
    public void run() {...}
}

람다식을 이용해서 Runnable 인터페이스의 익명 구현 객체를 생성하기

Runnable runnable = () -> {...}

람다식의 기본 문법

()의 매개 변수를 이용해서 {} 블록을 실행하자!

(타입 매개변수, ...) -> { 실행문; ... }

1. int 타입 변수 a를 이용해서 {} 블록을 실행하자 = a를 출력함

(int a) -> { System.out.println(a); }

 

2. 근데 매개 변수 타입은 런타임에 대입되는 값에 따라 자동으로 인식된다 → 람다식에서는 일반적으로 명시 안 함

(a) -> { System.out.println(a); }

 

3. 근데 또 매개 변수가 하나라면 () 괄호도 생략 가능

a -> { System.out.println(a); }

 

3-1.  근데 또 매개 변수가 없다고 괄호를 생략하면 X 없다고 명시해줘야 함

() -> { 실행문; }

 

4. {}에 return문만 들어가는 경우라면 return문 안 쓰고 {}에서 바로 작성할 수 있다

(x, y) -> { return x+y; }
//{} 블록에 return문밖에 없다
(x, y) -> x+y;
//로 간단하게 작성 가능

타겟 타입

람다식은 매개변수를 받아 {} 블록을 실행한다

→ 메서드 아닌가여?

→→ 메서드는 항상 클래스의 멤버로 선언된다

람다식은 메서드를 선언하는 것이 아니라, 메서드를 가지고 있는 객체를 생성하는 것

but what type

인터페이스 변수 = 람다식;
  • 람다식은 인터페이스 변수에 대입된다 = 람다식은 인터페이스의 익명 구현 객체를 생성한다
  • 인터페이스는 구현 클래스가 필요한다 → 람다식이 익명 구현 클래스를 생성하고 객체화 해줌!
  • 인터페이스는 추상적 → 람다식은 대입될 인터페이스에 따라 작성 방법이 달라짐

따라서, 람다식의 타겟 타입은 람다식이 대입될 인터페이스라고 정리한다


함수적 인터페이스

람다식은 하나의 메서드를 정의한다 → 인터페이스에 두 개 이상의 추상 메서드가 선언되어 있다면 람다식을 이용해서 구현 객체를 생성하는 것은 불가능하다

함수적 인터페이스: 하나의 추상 메서드가 선언된 인터페이스

(review: 함수적 인터페이스인지 확인해주는 애너테이션 @FunctionalInterface)

 

함수적 인터페이스가 가지고 있는 추상 메서드의 형태에 따라 달라지는 람다식의 작성 방법

1. No 매개 변수, No 리턴

  • 함수적 인터페이스 선언
@FunctionalInterface
public interface MyFunctionalInterface {
    public void method();
}
  • 매개 변수와 리턴 값이 없는 추상 메서드를 구현하는 람다식
public class MyFunctionalInterfaceExample {
    public static void main(String[] args) {
        
        MyFunctionalInterface fi;
        //인터페이스의 참조 변수 선언
        
        //람다식의 기본 형태        
        fi = () -> {
        	String str = "method call1";
            System.out.println(str);
        };
        fi.method();
        //람다식이 대입된 인터페이스의 참조변수로 method() 호출
        //method()는 람다식의 {}를 실행
        
        
        //{} 안에서 바로 실행문 작성 가능
        fi = () -> { System.out.println("method call2"); };
        fi.method();
        
        
        //실행문이 하나일 때 {} 생략 가능
        fi = () -> System.out.println("method call3"); 
        fi.method();        
        
    }
}

 

2. Yes 매개 변수

  • 함수적 인터페이스 선언
@FunctionalInterface
public interface MyFunctionalInterface {
    public void method(int x);
}
  • 매개 변수와 리턴 값이 없는 추상 메서드를 구현하는 람다식
public class MyFunctionalInterfaceExample {
    public static void main(String[] args) {
    
        MyFunctionalInterface example;
        
        fi = (x) -> {
            int result = x*2;
            System.out.println(result);
        };
        fi.method(2);

        fi = (x) -> { System.out.println(x*2); };
       	fi.method(2);
        
        fi = x -> System.out.println(x*2);
        fi.method(2);
    }
}

 

3. Yes 리턴

  • 함수적 인터페이스 선언
@FunctionalInterface
public interface MyFunctionalInterface {
    public void method(int x, int y);
}
  • 매개 변수와 리턴 값이 없는 추상 메서드를 구현하는 람다식
public class MyFunctionalInterfaceExample {
    public static void main(String[] args) {
    
        MyFunctionalInterface fi;
        
        //람다식이 대입된 인터페이스 참조 변수로 method() 호출
        
        fi = (x, y) -> {
            int result = x + y;
            return result;
        };
        System.out.println(fi.method(1, 3);

        fi = (x, y) -> { return x + y; };
        System.out.println(fi.method(1, 3);

        fi = (x, y) -> x + y;
        System.out.println(fi.method(1, 3);

        fi = (x, y) -> sum(x, y);
        System.out.println(fi.method(1, 3);

    }
    
    public static int sum(int x, int y){
        return (x+y);
    }
    
}

메서드 참조

메서드를 참조해 매개 변수의 정보 및 리턴 타입을 알아내어, 람다식에서 불필요한 매개 변수를 제거하는 것을 목적으로 한다.

  • 람다식으로 두 매개값을 받아 Math 클래스의 max 메서드를 호출하기
(left, right) -> Math.max(left, right);
  • 메서드 참조를 이용하기
Math :: max;
//Math 클래스의 max 메서드를 호출

메서드 참조도 람다식과 마찬가지로 인터페이스의 익명 구현 객체로 생성된다. 따라서 타겟 타입인 인터페이스의 추상 메서드가 어떤 매개 변수와 리턴 타입을 가지는지에 따라 달라지게 됨.

→ 두 개의 int 매개값을 받아 int 값을 리턴하는 IntBinaryOperator 인터페이스에 메서드 참조 대입

IntBinaryOperator operator = Math :: max;
더보기

+

IntBinaryOperator)

 

IntBinaryOperator (Java Platform SE 8 )

 

docs.oracle.com

+ 추가 공부: API 클래스,  표준API-함수적 인터페이스

 

1. 정적 메서드 참조

클래스 :: 메서드

e.g.

  • 정적 메서드 staticMethod
public class Calculator {
    public static int staticMethod(int x, int y ){
        return x+y;
    }
}
  • 람다식과 메서드 참조의 비교
import java.util.function.IntBinaryOperator;

public class MethodReferencesExample {
    public static void main(String[] args) {
        IntBinaryOperator operator;
		
        //람다식을 이용해 Calculator 클래스의 staticMethod 불러오기
        operator = (x, y) -> Calculator.staticMethod(x, y);
        System.out.println(operator.applyAsInt(1, 2));

        //메서드 참조를 이용해 Calculator 클래스의 staticMethod를 불러오기
        operator = Calculator :: staticMethod;
        System.out.println(operator.applyAsInt(3, 4));
    }
}

 

2. 인스턴스 메서드 참조

참조변수 :: 메서드

e.g.

  • 인스턴스 메서드 instanceMethod
public class Calculator {
    public int instanceMethod(int x, int y){
        return x+y;
    }
}
  • 람다식과 메서드 참조의 비교
package lambda;

import java.util.function.IntBinaryOperator;

public class MethodReferencesExample {
    public static void main(String[] args) {
        IntBinaryOperator operator;
        
        //인스턴스 메서드 사용을 위해 객체 생성
        Calculator cal = new Calculator();
        
        //람다식을 이용
        operator = (x, y) -> cal.instanceMethod(x, y);
        System.out.println(operator.applyAsInt(5, 6));
        
        //메서드 참조를 이용
        operator = cal :: instanceMethod;
        System.out.println(operator.applyAsInt(7, 8));
    }
}

 

3. 매개 변수의 메서드 참조

이전 경우: 람다식 외부의 클래스 멤버인 메서드를 호출함

람다식에서 제공되는 a 매개변수의 메서드를 호출해 b 매개변수를 매개값으로 사용하기

  • 람다식
//a 매개변수의 메서드 instanceMethod를 호출해서 b를 매개값으로 사용하기
(a, b) -> { a.instanceMethod(b); }
  • 메서드 참조
클래스 :: instanceMethod

e.g.

import java.util.function.ToIntBiFunction;

public class ArgumentMethodReferenceExample {
    public static void main(String[] args) {
        ToIntBiFunction<String, String> function;

		//람다식 이용하기
        function = (a, b) -> a.compareToIgnoreCase(b);
        System.out.println(function.applyAsInt("Java11", "JAVA11")); //0
		
        //메서드 참조 이용하기
        function = String :: compareToIgnoreCase;
        System.out.println(function.applyAsInt("Java11", "JAVA11")); //0

    }
}

 

4. 생성자 참조

생성자를 참조한다 = 객체를 생성한다

클래스 :: new

생성자가 여러개일 경우(오버로딩) 함수적 인터페이스의 추상 메서드와 동일한 매개 변수 타입과 개수를 갖는 생성자를 찾아 실행. 없으면 error

e.g.

public class Member {
    private String name;
    private String id;

    public Member(){
        System.out.println("Member() 실행");
    }
    
    public Member(String id){
        this.id = id;
        System.out.println("Member(String id) 실행");
    }
    
    public Member(String name, String id){
        this.name = name;
        this.id = id;
        System.out.println("Member(String name, String id) 실행");
    }
}
import java.util.function.BiFunction;
import java.util.function.Function;

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

        Function<String, Member> function1 = Member :: new;
        Member member1 = function1.apply("피자");
        //Member(String id) 실행		
        
        BiFunction<String, String, Member> function2 = Member :: new;
        Member member2 = function2.apply("피자", "치킨");
        //Member(String name, String id) 실행
        
    }
}

 


🐑

1. 함수적 인터페이스?

2. API