[Java] 람다식에 대하여

✨New Wisdom✨·2020년 12월 16일
1

📕 Java 📕

목록 보기
8/24
post-thumbnail

이 노트는 "윤성우의 열혈 java 프로그래밍" 책을 공부하면서
내가 이해한대로 다시 정리하면서 작성되었다.

자바를 기초부터 하나하나 체화하려고 정리하면서 공부하다 보니 진도가 늦은 것이 사실이다 ㅠ
그래도 끝까지 포기하지 않고 자바를 정복해보려 노력하겠다 🥲

람다와 함수형 인터페이스

자바는 객체지향 언어이다. 코드의 흐름 대부분에 클래스와 인스턴스가 존재한다.
하지만 프로그램을 작성하다 보면 "기능 하나를 정의해서 전달해야 하는 상황"이 존재한다.
그럴때 람다를 사용하면 좀 더 간결한 코드를 만들 수 있다.

매개변수가 있고 반환하지 않는 람다식

interface Printable {
    void print(String s);    // 매개변수 하나, 반환형 void
}

class OneParamNoReturn {
    public static void main(String[] args) {
        Printable p;

        p = (String s) -> {System.out.println(s);};    // 줄임이 없는 표현
        p.print("Lambda exp one.");

        p = (String s) -> System.out.println(s);    // 중괄호 생략
        p.print("Lambda exp two.");
        
        p = (s) -> System.out.println(s);    // 매ㅐ개변수 형 생략
        p.print("Lambda exp three.");

        p = s -> System.out.println(s);    // 매개변수 소괄호 생략
        p.print("Lambda exp four.");
    }
}
  • 메소드의 몸체가 하나의 문장으로 이루어져 있다면 중괄호의 생략이 가능하다.
  • 컴파일러 입장에서 매개변수의 타입을 유추할 수 있으니 자료형도 생략이 가능하다.
  • 매개변수가 하나일 경우에는 소괄호도 생략이 가능하다.

매개변수가 2개 이상일 경우는

interface Calculate {
    void cal(int a, int b);    // 매개변수 둘, 반환형 void
}

class TwoParamNoReturn {
    public static void main(String[] args) {
        Calculate c;
        c = (a, b) -> System.out.println(a + b);
        c.cal(4, 3);

        c = (a, b) -> System.out.println(a - b);
        c.cal(4, 3);

        c = (a, b) -> System.out.println(a * b);
        c.cal(4, 3);
    }
}

매개변수가 있고 반환하는 람다식

interface Calculate {
    int cal(int a, int b); // 값을 반환하는 추상 메소드
}

class TwoParamAndReturn {
    public static void main(String[] args) {
        Calculate c;
        c = (a, b) -> { return a + b; };
        System.out.println(c.cal(4, 3));
        
        c = (a, b) -> a + b;
        System.out.println(c.cal(4, 3));
    }
}
  • 메소드 몸체에 해당하는 내용이 return 문이면 문장이 하나더라고 중괄호 생략이 불가능하다.
  • 하지만 return 문이 메소드 몸체를 이루는 유일한 문장이면 (a, b)->a+b와 같이 작성할 수 있다.

example

interface HowLong {
    int len(String s);    // 값을 반환하는 메소드
}

class OneParamAndReturn { 
    public static void main(String[] args) {
        HowLong hl = s -> s.length();
        System.out.println(hl.len("I am so happy"));
    }
}

매개변수가 없는 람다식

매개변수가 없는 람다식은 매개변수를 표현하는 소괄호 안을 비우면 된다.

interface Generator {
    int rand();    // 매개변수 없는 메소드
}

class NoParamAndReturn {
    public static void main(String[] args) {
        Generator gen = () -> {
            Random rand = new Random();
            return rand.nextInt(50);
        };

        System.out.println(gen.rand());
    }
}
  • 둘 이상의 문장으로 이루어진 람다식은 중괄호로 반드시 감싸고, 값 반환할 때도 return 문을 반드시 써야한다.

함수형 인터페이스와 어노테이션

인터페이스에 추상 메소드가 딱 하나만 존재하는 것을 함수형 인터페이스라고 한다.
람다식은 이런 함수형 인터페이스를 기반으로만 작성될 수 있다.

@FunctionalInterface
interface Calculate {
	int cal(int a, int b);
}
  • FunctionalInterface은 함수형 인터페이스에 부합하는지를 확인하기 위한 어노테이션 타입이다.

둘 이상의 추상 메소드가 존재하면 함수형 인터페이스가 아니기 때문에 에러를 낸다.
하지만 static, default 선언이 붙은 메소드 정의는 함수형 인터페이스의 정의에 아무런 영향을 미치지 않는다.

람다식과 제네릭

인터페이스는 제네릭으로 정의할 수 있다.
다음은 제네릭으로 정의된 함수형 인터페이스를 대상으로 하는 람다식이다.

@FunctionalInterface
interface Calculate <T> { // 제네릭 기반의 함수형 인터페이스
    T cal(T a, T b);
}

class LambdaGeneric {
    public static void main(String[] args) {
        Calculate<Integer> ci = (a, b) -> a + b;
        System.out.println(ci.cal(4, 3));

        Calculate<Double> cd = (a, b) -> a + b;
        System.out.println(cd.cal(4.32, 3.45));
    }
}
  • 두 람다식은 동일하지만 참조변수의 자료형이 다른 관계로 이 둘은 전혀 다른 인스턴스의 생성으로 이어진다.

정의되어 있는 함수형 인터페이스

미리 정의되어 있는 함수형 인터페이스

default boolean removeIf(Predicate<? super E> filter)
  • Collection<E> 인터페이스에 정의된 디폴트 메소드 중 하나인 removeIf 메소드이다.

Predicte가 뭐야?

다음과 같이 정의되어 있는 제네릭 인터페이스이자 함수형 인터페이스이다.

@FunctionalInterface
public interface Predicate<T> {
	boolean test(T t);
}

이렇게 자바에서는 메소드의 반환형과 매개변수 선언에 차이를 둔 다양한 인터페이스들을 표준으로 정의하고 있다.

표준으로 정의된 대표적 함수형 인터페이스와 그 안에 있는 추상 메소드

  • Predicate<T> - boolean test(T t)
  • Supplier<T> - T get()
  • Consumer<T> - void accept(T t)
  • Funtion<T, R> - R apply(T t)

Predicate<T>

boolean test(T t); 

전달된 인자를 대상으로 true, false를 반환해야하는 상황에 유용하게 사용된다.

import java.util.List;
import java.util.Arrays;
import java.util.function.Predicate;

class PredicateDemo {
    public static int sum(Predicate<Integer> p, List<Integer> lst) {
        int s = 0;
        
        for(int n : lst) {
            if(p.test(n))
                s += n;
        }       
        
        return s;
    }
    
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 5, 7, 9, 11, 12);
    
        int s;
        s = sum(n -> n%2 == 0, list);
        System.out.println("짝수 합: " + s);

        s = sum(n -> n%2 != 0, list);
        System.out.println("홀수 합: " + s);

    }
}
  • public static int sum(Predicate<Integer> p, List<Integer> lst)에서 매개변수 선언을 볼 때 boolean test(Integer t) 메소드 정의에 해당하는 람다식을 작성해서 전달해야한다.

Supplier<T>

T get();

단순히 무언가를 반환해야 할 때 사용된다.

class SupplierDemo {
    public static List<Integer> makeIntList(Supplier<Integer> s, int n) {
        List<Integer> list = new ArrayList<>();    
        for(int i = 0; i < n; i++)
            list.add(s.get());
        return list;
    }
    
    public static void main(String[] args) {
        Supplier<Integer> spr = () -> {
            Random rand = new Random();
            return rand.nextInt(50);
        };

        List<Integer> list = makeIntList(spr, 5);
        System.out.println(list);

        list = makeIntList(spr, 10);
        System.out.println(list);
    }
}
  • public static List<Integer> makeIntList(Supplier<Integer> s, int n) 메소드는 정수를 담고 있는 컬렉션 인스턴스르 반환한다.
    첫 번째 인자에서 컬렌션 인스턴스에 담을 정수의 생성 방법을 결정할 수 있다.

Consumer<T>

void accept(T t);

전달 인자를 소비하는 령태로 매개변수와 반환형이 선언되어 있다.
(인자는 전달 받지만 반환하지 않는다.)
전달된 인자를 가지고 어떤 결과를 보여야 할 때 유용하게 사용된다.

import java.util.function.Consumer;

class ConsumerDemo {
   public static void main(String[] args) {
       Consumer<String> c = s -> System.out.println(s);
       
       c.accept("Pineapple");    // 출력이라는 결과를 보인다.
       c.accept("Strawberry");
   }
}

Funtion<T, R>

R apply(T t);

전달한 인자와 반환 값이 모두 존재하는 가장 보편적인 형태이다.

RemoveIf 메소드를 사용해보자

default boolean removeIf(Predicate<? super E> filter)
  • 컬렉션 인스턴스에 저장된 인스턴스를 다음 test 메소드의 인자로 전달했을 때true 가 되는 인스턴스는 모두 삭제한다.
    위 메소드의 매개 변수 선언인 Predicate<? super E>를 보면 ArrayList<Integer> 인스턴스를 생성하면, 그 안에 존재하는
    removeIf 메소드의 E는 다음과 같이 Integer로 결정된다.

정리중 ✍️

profile
🚛 블로그 이사합니다 https://newwisdom.tistory.com/

0개의 댓글