람다

박진선·2022년 11월 16일
0

람다란?

  • 함수형 프로그래밍 기법을 지원하는 자바의 문법요소이다.

  • 메서드를 하나의 '식(expression)' 으로 표현한 것으로, 코드를 매우 간결하면서 명확하게 표현할 수 있다는 큰 장점이 있다.

람다식 문법

  • 매개변수의 타입을 추론할 수 있는 경우 타입 생략 가능하다.

  • 매개변수가 하나인 경우에는 소괄호() 생략 가능하다.

  • 함수의 바디가 한줄로만 이루어진 경우에는 중괄호{} 및 return문 세미콜론; 생략이 가능하다.

  • 함수의 바디가 return 문 한줄로만 이루어진 경우에는 중괄호{}를 생략할 수 없고
    중괄호가 있으면 각 줄마다 세미콜론; 을 생략할 수 없다.

(int x) -> {return x;}; // 생략전
x -> x; // 매개변수가 하나이기 때문에 소괄호 생략, 바디가 한줄로만 이루어지 있기 때문에 중괄호 및 return문 생략
(x, y) -> { // 매개변수가 하나 이상이기 때문에 소괄호 생략 불가, 바디가 여러 줄로 작성되어 중괄호, 세미콜론, return문 생략 불가
x = 30;
y = 40;
return x * y;
};

함수형 인터페이스

  • 추상 메소드를 하나만 가지고 있는 인터페이스를 뜻한다. 그리고 람다식은 이러한 함수형 인터페이스를 기반으로만 작성이 될 수 있다. 즉, 함수형 인터페이스를 사용하는 이유는 람다식은 함수형 인터페이스로만 접근이 가능하기 때문에 사용한다.

  • @FunctionalInterface 애너테이션을 인터페이스에 붙혀주면 더 명시적으로 구분할 수 있고 함수형 인터페이스 규칙을 위반하면 컴파일시 에러가 발생하게 된다.

  • 아래의 코드는 자바에서 제공하는 기본 함수형 인터페이스 이다. 자바에서는 자주 사용할만한 함수형 인터페이스를 이미 정의 해두었기 때문에 특별한 경우가 아니면 해당 인터페이스를 사용하면 된다.

  • 함수형 인터페이스를 보면 제네릭을 사용하여 Function<Integer, Integer> 에서 매개변수 타입과 반환타입이 정해진다. main 메소드 내부에서 함수형 인터페이스 참조 변수를 선언하고 람다식을 대입하면 자동으로 함수형 인터페이스의 단 1개뿐인 추상메서드 apply 메소드가 정의되면서 객체를 생성한다.

  • 람다식의 형태는 매개 변수를 가진 코드 블록이므로 마치 자바의 메소드를 선언하는 것처럼 보인다. 하지만, 자바는 메소드를 단독으로 선언할 수 없고 항상 클래스의 구성 멤버로 선언하기 때문에 람다식은 단순히 메소드를 선언하는 것이 아니라 이 메소드를 가지고 있는 객체를 생성해 낸다. 그리고 이 객체의 타입은 다름 아닌 인터페이스이다. "인터페이스 변수 = 람다식;"과 같이 선언하는 것이다. 즉, 람다식은 인터페이스의 익명 구현 객체를 생성한다는 말이 된다.

@FunctionalInterface
public interface Function<T, R> {
	 R apply(T t);
}

public class Main {
    public static void main(String[] args) {
		Function<Integer, Integer> function = n -> n * 10;
        System.out.println(function.apply(20)); // 200이 출력된다.
    }
}

자바에서 제공하는 함수형 인터페이스 종류

Function<T, R>

  • R apply(T t) : T 타입을 받아서 R 타입을 반환하는 추상 메소드

BiFunction<T, U, R>

  • R apply(T t, U u) : T, U 타입을 받아서 R 타입을 반환하는 추상 메소드

Consumer<T>

  • accept(T t) : T 타입을 받아서 받아서 로직을 수행 후 반환값은 없는 추상 메소드

Supplier<T>

  • T get() : 매개변수 없이 T 타입을 반환하는 추상 메소드

Predicate<T>

  • boolean test(T t) : T 타입을 받아서 boolean을 리턴하는 함수 인터페이스

UnaryOperator<T> extends Function<T, T>

  • Function<T, R>의 특수한 형태로 입력값 하나를 받아 동일한 타입을 리턴하는 함수 인터페이스

BinaryOperator<T> extends BiFunction<T,T,T>

  • BiFunction<T, U, R>의 특수한 형태로, 동일한 타입의 입력값 두 개를 받아 같은 타입을 리턴하는 함수 인터페이스

메소드 레퍼런스

  • 기존에 람다 표현식(Lambda Expression)은 직접 인라인으로 기능들을 구현했다. 하지만, 매번 소소한 기능부터 복잡한 기능까지 새로 구현을 하는 것은 매우 비효율적 이다.
    람다를 통해 기존의 메소드, 생성자를 호출할 때 메소드 레퍼런스를 사용해 매우 간결하게 표현할 수 있다.

메소드 참조 방법

스태틱 메소드참조 클래스::메소드
인스턴스 메소드객체 참조변수::메소드
생성자참조 클래스::new
class Member{
	private String name;
	private int age;
    
    public Member(String name, int age) {
		this.name = name;
		this.age = age;
	}
    public int getAge() {
		return age;
	}
	public String getName() {
		return name;
	}
	public static String hi(String name){
		return "hi " + name;
	}
}
class Example {
	public void test(){
    	UnaryOperator<String> unaryOperator = Member::hi; // 스태틱 메소드 참조사용
    	System.out.println(unaryOperator.apply("Json")); // hi Json 출력

    	Member member = new Member("Json",30);
    	Supplier supplier = member::getName; //인스턴스 메소드참조 사용
    	System.out.println(supplier.get()); //Json 출력

		BiFunction<String,Integer,Member> biFunction = Member::new; // 생성자 참조 사용
    	Member member1 = biFunction.apply("Json",30);
    	System.out.println(member1.getName()); // Json 출력
    	System.out.println(member1.getAge()); // 30 출력
    }
}

메소드참조를 사용할 때 호출하는 메소드의 매개변수 개수와 함수형인터페이스의 추상 메소드 매개변수의 개수를 일치하게 잘 고려하여 호출해야 한다.
인스턴스 메소드 getName은 매개변수가 없고 Supplier 인터페이스의 추상 메소드 get 도 매개변수가 없기 때문에 컴파일 에러가 없지만 만약 Consumer 함수형 인터페이스로 변경할 경우 Consumer의 추상메소드 accept 는 매개변수를 하나 필요로 하기에 컴파일 에러를 발생시킨다.

profile
주니어 개발자 입니다

0개의 댓글