애매한 잡학사전

[자바8] 람다 표현식 - 1장 본문

DEV/JAVA

[자바8] 람다 표현식 - 1장

거대한 개발자 2022. 2. 13. 19:34
반응형
'카이 호스트만의 코어 자바 8'을 기준으로 정리하였습니다.

1. 람다 표현식

    - 1 ~ n번 실행할 수 있게 전달하는 코드 블록

    - 자바에는 함수 타입이 없기 때문에 객체로 표현

    - 파라미터 변수가 있는 표현식

       : 오래전 컴퓨터가 없던 시절에 논리학자 안론조 처치는 수학 함수로 효과적인 계산을 할 수 있도록 관련 내용을 공식화 하려고 했다.

       : 파라미터를 표기하는데 그리스 문자 람다(λ)를 사용했다.

       : 공신력 있는 수학 원리 책에서 함수 파라미터를 나타내는 데 악센트 ^를 사용했는데 알론조 처치는 여기서 영감을 얻어

         대문자 람다(Λ)를 사용했고, 나중에는 소문자 람다(λ)로 바꿨다. 

 

2. 람다 표현식 문법

    2-1. 일반 자바 메서드

public int strLength(String first, String second){
    return first.length() - second.length();
}

    2-2. 람다 표현식

(String first, String second) -> first.length() - second.length();

        - 단순한 코드 블록이지만 해당 코드에 전달해야 하는 변수의 명세를 갖췄다.

        - 결과 타입은 명시하지 않지만 컴파일러는 구현부로부터 결과 타입을 추론해서 기대하는 타입과 일치하는지 검사한다.

        - 위의 표현식은 기대하는 결과가 int 타입인 문맥에서 사용할 수 있다.

 

    2-3. n라인의 람다 표현식

(String first, String second) -> {
    int difference = first.length() - second.length();
    
    if (difference < 0) {
        return -1;
    } else if(difference > 0) {
        return 1;
    } else {
        return 0;
    }
}

        - 람다 표현식의 구현부에서 표현식을 여러 줄로 작성할 때는 메서드를 작성하는 것처럼 작성한다.

 

    2-4. 파라미터가 없는 람다 표현식

Runnable task = () -> { for(int i=0; i < 1000; i++) doWork(); }

        - 람다 표현식이 파라미터를 받지 않으면 파라미터가 없는 메서드처럼 빈 괄호를 붙인다.

 

    2-5. 파라미터 타입이 없는 람다 표현식

Comparator<String> comp = (first, second) -> first.length() - second.length();

        - 람다 표현식을 문자열 비교자에 할당하므로 컴파일러는 first와 second를 문자열이라고 추론할 수 있기 때문에 생략할 수 있다.

 

    2-6. 파라미터가 1개인 람다 표현식

EventHandler<ActionEvent> listener = event -> System.out.println("Oh noes!");

/* (event) 또는 (ActionEvent event) 대신 사용 가능 */

 

3. 함수형 인터페이스

    - 추상 메서드가 한 개만 포함된 인터페이스에만 사용할 수 있는 인터페이스

    3-1. 함수형 인터페이스 전달

/* Arrays.sort 메서드 */
public static <T> void sort(T[] a, Comparator<? super T> c) {
    if (c == null) {
        sort(a);
    } else {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            TimSort.sort(a, 0, a.length, c, null, 0, 0);
    }
}

/* Arrays.sort 람다 전달 */
Arrays.sort(words, (first, second) -> first.length() - second.length());

        - Arrays.sort 메서드의 두 번째 파라미터 변수는 Comparator<String>을 구현하는 클래스의 객체를 받는다.

        - Arrays.sort 메서드에 위와 같이 람다를 전달하면 람다 표현식의 구현부를 실행한다.

    3-2. 함수 리터럴 참고

/* 자바스크립트 함수 타입 선언 */
var compare = function(first, second){
    return first.length - second.length;
};

/* 함수 호출 */
compare("test1", "test");
/* 예상결과 : 1 */

        - 함수 리터럴을 지원하는 대부분의 프로그래밍 언어는 (String, String) -> int 처럼 함수 타입을 선언하고, 이 함수 타입으로 변수를 선언한다. 그러면 함수를 해당 변수에 저장하고 호출할 수 있다.

String[] words = {"String", "Integer", "Float"};

Comparator<String> comp = (first, second) -> first.length() - second.length();
Arrays.sort(words, comp);
/* or */
Arrays.sort(words, (first, second) -> first.length() - second.length());

        - 자바에서는 람다 표현식을 함수형 인터페이스 타입 변수에 저장해서 해당 인터페이스의 인스턴스로 변환하는 것만 가능하다.

    3-3. 자주 사용하는 함수형 인터페이스

함수형 인터페이스 파라미터 타입 반환 타입 추상 메서드 이름 설명 다른 메서드
Runnable 없음 void run 인자나 반환 값 없이 액션을 수행한다.  
Supplier<T> 없음 T get T 타입 값을 공급한다.  
Consumer<T> T void accept T 타입 값을 소비한다. andThen
BiConsumer<T, U> T, U void accept T 와 U 타입 값을 소비한다. andThen
Function<T, R> T R apply T 타입 인자를 받는 함수다. compose, andThen, identity
BiFunction<T, U, R> T, U R apply T 와 U 타입 인자를 받는 함수다. andThen
UnaryOperator<T> T T apply T 타입에 작용하는 단항 연산자다. compose, andThen, identity
BinaryOperator<T> T, T T apply T 타입에 작용하는 이항 연산자다. andThen, maxBy, minBy
Predicate<T> T boolean test 불 값을 반환하는 함수다. and, or, negate, isEqual
BiPredicate<T, U> T, U boolean test 두 가지 인자를 받고 불 값을 반환하는 함수다. and, or, negate

    3-4. 기본 타입용 함수형 인터페이스

함수형 인터페이스 파라미터 타입 반환 타입 추상 메서드 이름
BooleanSupplier 없음 boolean getAsBoolean
pSupplier 없음 p getAsP
pConsumer p void accept
ObjPConsumer<T> T, p void accept
pFunction<T> p T apply
pToQFunction p q applyAsQ
ToPFunction T p applyAsP
ToPBiFunction<T, U> T, U p applyAsP
PUnaryOperator p p applyAsP
PBinaryOperator p, p p applyAsP
PPredicate p boolean test

    : p, q는 int, long, double이다. P, Q는 Int, Long, Double이다.

 

4. 메소드 참조

/* 대소문자 구분 없이 문자열 정렬 */
Arrays.sort(strings, (x, y) -> x.compareToIgnoreCase(y));

/* 위의 표현식 대신 사용 가능 */
Arrays.sort(strings, String::compareToIgnoreCase);

        - :: 연산자는 메서드 이름과 클래스를 분리하거나 메서드 이름과 객체의 이름을 분리한다.

        - 클래스::인스턴스메서드, 클래스::정적메서드, 객체::인스턴스메서드 3가지 형태로 사용할 수 있다.

    4-1. 클래스::인스턴스메서드

    - 첫 번째 파라미터가 메서드의 수신자가 되고, 나머지 파라미터는 해당 메서드로 전달된다.

        : String::compareToIgnoreCase는 (x, y) -> x.compareToIgnoreCase(y)

    4-2. 클래스::정적메서드

    - 모든 파라미터가 정적 메서드로 전달된다.

        : Objects::isNull은 x -> Objects.isNull(x)

    4-3. 객체::인스턴스메서드

    - 주어진 객체에서 메서드가 호출되며, 파라미터는 인스턴스 메서드로 전달된다.

        : System.out::println은 x -> System.out.println(x)

 

= 같은 이름으로 오버로드된 메서드가 여러 개일 때 컴파일러는 문맥을 통해 어느 것을 의도했는지 알아내려 한다. 

= 예를 들어 println 메서드는 여러 가지 버전이 있다. ArrayList<String>의 forEach 메서드에 println을 전달하면 println(String) 메서드가 선택된다.

 

5. 생성자 참조

    - 생성자 참조는 메서드의 이름이 new라는 점만 제외하면 메서드 참조와 같다.

    - 클래스에 생성자가 두 개 이상 있을 때는 문맥을 통해 호출할 생성자를 선택한다.

    5-1. 일반 생성자 참조

        - Employee::new 로 사용한다.

    5-2. 배열 생성자 참조

        - Employee[]::new 로 사용 하며 n -> new Employee[n] 과 같다.

        - 자바에서는 제네릭 타입으로 배열 생성을 할 수 없어서 배열 생성자 참조를 사용하면 배열을 좀 더 확장해서 사용할 수 있다.

 

/* 이름 리스트 생성 */
List<String> names = Arrays.asList("A", "B", "C");

/* 일반 생성자 참조 */
Stream<Employee> stream = names.stream().map(Employee::new);

/* 배열 생성자 참조를 이용해서 이름 출력 */
for(Employee name : stream.toArray(Employee[]::new)){
    System.out.println(name.getName());
}

/* Employee 클래스 */
public class Employee {
    String name;

    public Employee(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }
}

 

 

 

 

[자바8] 람다 표현식 - 2장

'카이 호스트만의 코어 자바 8'을 기준으로 정리하였습니다. [자바8] 람다 표현식 - 1장 '카이 호스트만의 코어 자바 8'을 기준으로 정리하였습니다. 1. 람다 표현식 - 1 ~ n번 실행할 수 있게 전달하

dev-gabriel.tistory.com

 

Comments