스터디할래 - 15주차 과제: 람다식

 https://github.com/whiteship/live-study

 

 

👍목표

 

자바의 람다식에 대해 학습하세요.

 

📖학습할 것 (필수)

 

📌 람다식 사용법


📌 함수형 인터페이스

 

📌 Variable Capture

 

 

📌 메소드, 생성자 레퍼런스

 


💡 람다식 사용법

 

람다식이란 메서드를 하나의 식으로 표현한 것으로 자바8부터 람다식(Lambda Expression)을 지원하기 시작했다. 

 

람다식의 장점으로는 자바 코드가 간결해지고 Collection의 Element를 필터링하거나 매핑해서 원하는 결과를 비교적 쉽

 

게  집계할 수 있다.  

// 람다식 작성 예시

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

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

 

 

매개 변수 타입은 런타임에 대입되는 값에 따라 추론이 가능하므로 타입을 생략할 수 있다.

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

 

하나의 매개 변수만 있다면 소괄호를 생략할 수 있다.

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

 

 

또한 실행문이 하나라면 중괄호도 생략 할 수 있다.

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

 

리턴 값이 존재할 경우 { } return 문으로 결과 값을 지정 할 수 있으며, return 만 할 경우 { }와 return 을 생략 할 수 있다.

(n1, n2) -> {
    return n1 + n2;
}

(n1, n2) -> n1 + n2;

 

 

주의 할 점으로 매개 변수가 존재하지 않는 람다식에서는 반드시 () 소괄호를 써야한다.

 

Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("익명객체 구현");
            }
};

Runnable runnable = () -> System.out.println("runnable lambda");

 

 

위의 코드는 Runnable 인터페이스를 익명 객체 구현 방식과 람다 표현식 두 가지 방식으로 객체 생성을 보여주고

 

있다.

 

 

💡 함수형 인터페이스

 

 

함수형 인터페이스란 추상 메서드를 오직 1개만 가지는 인터페이스를 말한다. (static method 제외) 

 

함수형 인터페이스를 작성할 때 2개 이상의 추상 메서드가 선언되지 않도록 @FunctionalInterface 에노테이션을 붙여 컴

 

파일 타임에 체크할 수 있으며 2개 이상의 추상 메서드가 선언될 경우 컴파일 오류가 발생한다.

 

@FunctionalInterface 어노테이션을 명시적으로 선언하지않고 추상 메서드가 1개 뿐이라면 해당 인터페이스는

 

'함수형 인터페이스'라고 할 수 있다.

 

 

@FunctionalInterface 어노테이션 구현을 타고 들어가면 Retention은 RUNTIME, Target은 TYPE으로 되어 있는 것을 확인 할 수 있다.

 

 

추상 메서드를 2개 이상 작성할 경우 Multiple non-overring... 의 에러 메시지 출력

 

 

static 메서드의 선언 갯수는 함수형 인터페이스 정의에 위배되지 않는 것을 확인할 수 있다.

 

 

public class Main {

    public static void main(String[] args) {
    
        FunctionalInterfaceSample functionalInterfaceSample;
        functionalInterfaceSample = () -> {
            System.out.println("함수형 인터페이스 메서드 호출");
        };
        functionalInterfaceSample.functionalMethod();
        FunctionalInterfaceSample.functionalMethod2();
        FunctionalInterfaceSample.functionalMethod3();
    }
}

Output:
함수형 인터페이스 메서드 호출
static method2
static method3

 

위의 코드는 함수형 인터페이스에 선언한 추상메서드를 람다식으로 구현하여 호출하였고,  인터페이스에서 정의한

 

static 메서드들을 호출한 결과를 보여주고 있다.

 

💡 Variable Capture

 

Variable Capture란 람다식 내의 변수 혹은 파라미터로 넘겨진 변수가 아닌 지역 변수를 참조하는 행동을 Lambda

 

Capturing이라고 하며 주의할 점이 2가지 존재한다.

 

  1. 지역 변수는 final로 선언 되어야 한다
  2. final이 아닌 지역 변수의 경우 값이 변경 되어선 안된다. 

 

final 상수인 localFinalValue와 final 키워드를 사용하지는 않았지만 값이 변경되지 않은 localValue1은 문제 없이 동작하

 

는 것을 확인 할 수 있으며, 값이 메서드 내에서 변경된 localValue2는 " Variable used in lambda expression should be

 

final or effectively final" 에러 메시지를 출력하는 것을 볼 수 있다.

 

 

 

값을 선언과 동시에 초기화하는 변수 뿐만 아니라 선언만 한 후 코드 중간에 값을 초기화 하는 지역 변수도 문제 없이 동

 

작하며 Lambda Capturing 주의점2에서와 마찬가지로 값만 변경하지 않으면 제대로 동작한다.

 

 

 

💡 메소드, 생성자 레퍼런스

 

메서드 레퍼런스는 말 그대로 메서드를 참조해서 매개 변수의 정보와 반환 타입을 알아내서 불필요한 매개 변수를 제거

 

할 수 있다. 

 

// 사용 방법
// 1) 클래스이름::메서드이름

// 2) 참조변수이름::메서드이름

 

메서드 참조 방식에는 3가지가 있다.

 

  • 정적 메서드 참조

정적 메서드 참조할 경우 클래스 이름 뒤에 :: 기호를 붙이고 메서드 이름을 기술하면 된다.

 

 

  • 인스턴스 메서드 참조

인스턴스 메서드일 경우 먼저 객체를 생성하고 참조 변수 뒤에 :: 기호를 붙인 후 인스턴스 메서드 이름을 기술하면 된다.

 

 

 

  • 매개변수의 메서드 참조

매개 변수 메서드 참조 방식도 static 메서드 참조와 동일하지만 매개 변수의 메서드를 호출해서 다른 매개 변수를 매개

 

값으로 사용하기 때문에 다른 코드가 실행된다.

 

 

import java.util.function.Function;

class ReturnNumber {
    static int staticMethod(int number) {
        System.out.println("staticMethod() 호출");
        return number;
    }

    int instanceMethod(int number) {
        System.out.println("instanceMethod() 호출");
        return number;
    }
}

public class Main {

    public static void main(String[] args) {

        Function<Integer, Integer> staticMethod = ReturnNumber::staticMethod;
        Integer apply = staticMethod.apply(10);
        System.out.println("apply = " + apply);

        ReturnNumber returnNumber = new ReturnNumber();
        Function<Integer, Integer> instanceMethod = returnNumber::instanceMethod;
        Integer apply1 = instanceMethod.apply(10);
        System.out.println("apply1 = " + apply1);

    }
}

Output:
staticMethod() 호출
apply = 10
instanceMethod() 호출
apply1 = 10

 

 

 

 

생성자 참조

 

 

생성자 참조는 객체 생성을 의마하며 사용 방법은 다음과 같다.

 

클래스 :: new;

 

 

메서드 참조를 이용해서 매개 변수가 없는 생성자와 있는 생성자 2가지 방식의 생성자 참조 방법은 아래와 같다.

 

import java.util.function.Function;
import java.util.function.Supplier;

public class Main {

    public static void main(String[] args) {

        Function<String, Person> function = Person::new;
        Person person1 = function.apply("홍길동");

        System.out.println("===========");

        Supplier<Person> supplier = Person::new;
        Person person = supplier.get();
    }
}

Output:
매개 변수가 존재하는 생성자 호출. 내 이름은 홍길동
===========
기본 생성자 호출

 

Function 함수형 인터페이스의 apply를 이용해서 매개변수가 있는 생성자에 name 인자를 전달하여 Person 객체를 생성

 

하였으며 Supplier 함수형 인터페이스를 이용하여 파라미터가 없는 Person 객체를 생성하였다.

 

 

References

이것이 자바다(신용권 저) 
tourspace.tistory.com/6 (Variable Capture)
perfectacle.github.io/2019/06/30/java-8-lambda-capturing/ (Lambda Capturing)

TCP School