Java 람다식

김정훈·2024년 4월 30일

Java

목록 보기
30/48

람다식(Lamba expression)

1. 람다식이란?

참고하기)
지역내부 인터페이스 객체

  • 메서드(함수)를 하나의 식으로 표현
  • 함수형 프로그래밍
    • 함수란?
      • 하나의 기능(단일기능)
        - 함수는 값으로 사용
        - 매개변수 : 사용자 정의 기능
        - 반환값 : 참고) 자바스크립트 - 클로저, 새로운 함수를 만드는 함수인 팩토리 함수
    • 자바는 함수는 값으로 사용 불가
      • 인터페이스의 객체가 되는 조건을 이용
      • 형식을 단순화
public class Ex01 {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("이름1","이름2","이름3");
        /*
        names.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
         */
        //자바에서는 함수가 함수이기 때문에 지역내부에 인터페이스가 객체를 활용하여 메서드를 객체로.
        //
        names.forEach(s -> System.out.println(s));
    }
}

2. 람다식 문법 살펴보기

메서드를 하나의 식으로 짧게 표현한 문법 / 용도가 제한적이므로 굳이 길게 작성❌
👉 최대한 짧게 쓰는게 가장 좋은 방식
1) 접근 제어자, 반환값 타입, 함수명을 생략
2) 매개변수 정의 부분과 함수 구현 부분({ }) 사이에 -> 추가
3) 매개변수의 자료형 생략 가능
4) 구현코드가 한줄일때는 { ... } 생략 가능, return 예약어도 생략 해야 된다.
5) 최대한 짧게 쓰는 방향(변수명도 한 글자로 하는 것이 권장)

3. 람다식 사용하기

4. 함수형 인터페이스(Functional Interface)

-단일기능, 단일역햘, 형식상 제한 조건
: 추상 메서드를 1개만 정의
👉 명확한 형식 제어를 위한 애노테이션
@FunctionalInterface

public interface Calculator {
    int add(int num1, int num2);
    int minus(int num1, int num2); 
    //함수형 프로그래밍을 하는 람다는 단일기능만 가능하다.그래서 오류
    //인터페이스상에서는 문제가 없지만. 메인함수에서 사용하게되면 어느 추상메서드를 가르키는지 잘알지 못한다.

}

public class Ex02 {
    public static void main(String[] args) {
        int c = 10;
        Calculator cal = (a, b) -> { //오류발생
            return a+b+c;
        };
        int result = cal.add(10,20);
        System.out.println(cal.add(10,20));
    }
}

@FunctionalInterface

package exam02;

@FunctionalInterface //오류발생
public interface Calculator {
    int add(int num1, int num2);
    int minus(int num1, int num2); 
    //함수형 프로그래밍을 하는 람다는 단일기능만 가능하다.그래서 오류
    //인터페이스상에서는 문제가 없지만. 메인함수에서 사용하게되면 어느 추상메서드를 가르키는지 잘알지 못한다.

}

5. 함수형 인터페이스 타입의 매개변수와 반환 타입

6. java.util.function패키지

1) 매개변수가 X, 반환값 1개
Supplier<T>
: T get()

2) 매개변수가 1개, 반환값 0개
Consumer<T>
: void accept(T t)

3) 매개변수가 1개 반환값도 1개
Function<T,R>
: R apply(T t)

4) 매개변수가 1개, 반환값은 boolean(논리값)
Predicate<T>
: boolean test(T t)

매개변수 2개

1) 매개변수가 2개, 반환값 X
BiConsumer<T, U>
: void accept(T t, U u)

2) 매개변수가 2개, 반환값 1개
BiFunction<T,U,R>
: R apply(T t, U u)

3) 매개변수가 2개, 반환값 boolean
BiPredicate<T, U>
: boolean test(T t, U u)

자료형동일

매개변수가1, 반환값1 -> 자료형이 모두 동일
UnaryOperator<T> : 상위 인터페이스 Function<T, T>
T apply(T t)

매개변수가 2개, 반환값1 -> 자료형이 모두 동일
BinaryOperator<T> : 상위 인터페이스 BiFunction<T, T, T>
T apply(T t1, T t2)

문제발생
1. 자료형 반복
2.연산을 위해서 Interger -> int (언박싱)
반환값 Integer로 변환(오토박싱),
int result로 변환 -> (언박생)
-> 성능저하, Wrapper클래스를 쓰지말고 연산은 그냥 기본형으로 하는게 좋다!
자료형 반복및 성능저하

public class Ex02 {
    public static void main(String[] args) {
        BiFunction<Integer, Integer, Integer> calc = (a, b) -> a + b;
        int result = calc.apply(10,20);
        System.out.println(result);
    }
}

자료형 반복 해결 BinaryOperator<T>

public class Ex03 {
    public static void main(String[] args) {
        BinaryOperator<Integer> calc = (a, b) -> a + b;
        int result = calc.apply(10,20);
    }
}

기본형 타입의 함수형 인터페이스

DoubleToLongFunciton
👉 double : 매개변수, long : 반환값
ToIntFunction<T>
👉 int : 반환값, T : 매개변수
IntFunction<R>
👉 int : 매개변수, R : 반환값
IntBinaryOperatior
👉int : 매개변수, int : 반환값
Wrapper클래스의 성능저하 문제해결
Wrapper클래스를 사용하지않고 기본형으로 사용

public class Ex04 {
    public static void main(String[] args) {
        IntBinaryOperator calc = (a,b) -> a+b;
        int result = calc.applyAsInt(10,20);
        System.out.println(result);
    }
}

7. Function의 합성과 Predicate의 결합

함수 결합

Function
		andThen(Function ... )
		f.andThen(g) -> f -> g 

		
		compose(..)
		g.compose(f) -> f -> g
public class Ex05 {
    public static void main(String[] args) {
        Function<String, Integer> func1 = s -> s.length();
        Function<Integer, Integer> func2 = x -> x * x;
        Function<String, Integer> func3 = func1.andThen(func2); //fun1과 fun2를 결합 fun1을 먼저 수행하고 fun2
        Function<String, Integer> func4 = func2.compose(func1); //fun1과 fun2를 결합
        int len1 = func3.apply("가나다");
        int len2 = func4.apply("가나다라마");
        System.out.println(len1);
        System.out.println(len2);

    }
}
  • Predicate
    • and
    • or
    • negate
public class Ex06 {
    public static void main(String[] args) {
        IntPredicate cond1 = x -> x >= 10;
        IntPredicate cond2 = x -> x <=100;
        IntPredicate cond3 = cond1.and(cond2);// x >= 10 && x <= 100
        IntPredicate cond4 = cond2.negate(); // x>100
        System.out.println(cond3.test(50));
        System.out.println(cond3.test(150));
        System.out.println(cond4.test(150));
    }
}

8. 메서드 참조

  • 람다식을 더욱 간결하게 표현
  • 클래스명::메서드명
public class Ex07 {
    public static void main(String[] args) {
        ToIntFunction<String> func1 = s -> s.length();
        ToIntFunction<String> func2 = String::length;

        BiPredicate<String, String> cond1 = (s1,s2) -> s1.equals(s2);
        BiPredicate<String, String> cond2 = String::equals;

    }
}
public class Ex08 {
    public static void main(String[] args) {
        Supplier<Book> s1 = () -> new Book();
        Supplier<Book> s2 = Book::new;
        Book b1 = s2.get();
        System.out.println(b1);

    }
}
public class Ex09 {
    public static void main(String[] args) {
        List<String> alpha = Arrays.asList("abc","def","ghi");
        //String[] upperAlpha = alpha.stream().map(String::toUpperCase).toArray(i ->new String[i]);
        String[] upperAlpha = alpha.stream().map(String::toUpperCase).toArray(String[]::new);

        System.out.println(Arrays.toString(upperAlpha));
    }
}
  • 참조변수::메서드명
public class Book {
    private String title;

    public String getTitle() {
        return title;
    }

    public void printTitle(){
        Supplier<String> t1 = () -> getTitle();
        Supplier<String> t2 = this :: getTitle;

    }
}
profile
안녕하세요!

0개의 댓글