티스토리 뷰

함수형 인터페이스(Functional Interface)란?

추상 메소드를 단 하나만 가지고 있는 SAM (Single Abstract Method) 인터페이스입니다.

@FuncationInterface 애노테이션을 가지고 있습니다.

 

함수형 인터페이스를 더 알아보기 전에 우선 람다 표현식 및 메소드 레퍼런스부터 알아보겠습니다.

 

람다 표현식(Lambda Expressions)이란?

함수형 인터페이스의 인스턴스를 만드는 방법으로 쓰일 수 있습니다.

메소드 매개변수, 리턴 타입, 변수로 만들어 사용할 수도 있습니다.

그리고 코드를 확연히 줄일 수 있습니다.

RunSomething runSomething = new RunSomething() {
    @Override
    public int doIt(int number) {
        return number + 10;
    }
};

RunSomething rambda = number -> number + 10;

 

표현식 : ( 인자 리스트 ) -> { 바디 }

인자 리스트 

  • 인자가 없을 때 : ()
  • 인자가 한 개 일 때 : (one) 또는 one
  • 인자가 여러개 일 때 : (one, two) 
  • 인자의 타입은 생략 가능하고 명시할 수도 있습니다. : (Integer one, Integer two)

바디 

  • 화살표 오른쪽에 함수 본문을 정의합니다.
  • 여러 줄인 경우에 {}를 사용해서 묶습니다.
  • 한 줄인 경우에 생략 가능하며, return도 생략이 가능합니다.

변수 캡처 (Variable Capture)     

  • 로컬변수캡처
    final이거나 effective final 인 경우에만 참조할 수 있습니다.
    concurrency 문제가 생길 수 있어서 컴파일에러가 발생합니다.
    public static void main(String[] args) {
            int baseNumber = 10;
    
            RunSomething rambda = number ->{
                return baseNumber;
            };
    }
  • effective final
    자바 8부터 지원하는 기능으로 사실상" final인 변수입니다.
    final 키워드 사용하지 않은 변수를 익명 클래스 구현체 또는 람다에서 참조할 수 있습니다.
  • 익명 클래스 구현체와 달리 ‘쉐도 윙(Shadowing)’ 하지 않습니다.
    익명 클래스는 새로 스코프를 만들지만, 람다는 람다를 감싸고 있는 스코프와 같습니다.
    그래서 람다 스코프와 람다를 감싸고 있는 스코프(main Method)에 똑같은 변수명을 쓰지 못합니다.

    • 쉐도 윙이란 ?
      외부 클래스의 변수명과 내부 클래스의 변수명 그리고 내부 클래스 내의 메서드 파라미터명이 같을 경우
      외부 클래스의 변수가 가려지면서, 내부클래스의 변수나 내부 클래스 내의 파라미터가 쓰여집니다.

메소드 레퍼런스란?

람다가 하는 일이 기존 메소드 또는 생성자를 호출하는 거라면, 메소드 레퍼런스를 사용하면 람다와 다르게 간결하게 표현할 수 있다.

public class Greeting {

    private String name;

    public Greeting() {

    }

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

    public String getName() {
        return name;
    }

    public String hello(String name){
        return "hello " + name;
    }

    public static String hi (String name){
        return "hi " + name;
    }
}
// 스태틱 메소드 참조
UnaryOperator<String> hi = Greeting::hi;

// 특정 객체의 인스턴스 메소드 참조
Greeting greeting = new Greeting();
UnaryOperator<String> hello = greeting::hello;

// 임의 객체의 인스턴스 메소드 참조
String [] name = {"minkyu", "com", "example"};
Arrays.sort(name, String::compareToIgnoreCase);

//생성자 참조
Supplier<Greeting> newGreeting = Greeting::new;

메소드 참조하는 방법

  • 스태틱 메소드 참조 > 타입::스태틱 메소드
  • 특정 객체의 인스턴스 메소드 참조 > 객체 레퍼런스::인스턴스 메소드
  • 임의 객체의 인스턴스 메소드 참조 > 타입::인스턴스 메소드
  • 생성자 참조 > 타입::new

메소드 또는 생성자의 매개변수로 람다의 입력값을 받습니다.

리턴 값 또는 생성한 객체는 람다의 리턴 값입니다.

 


자바에서 기본적으로 제공하는 함수형 인터페이스가 있습니다. 

java.util.function 패키지 중 자주 사용할만한 함수 인터페이스들을 자세히 살펴보겠습니다.

 

@Test
public void functionTest(){
    Function<Integer, Integer> plus10 = (number) -> number + 10;
    Function<Integer, Integer> multiply2 = (number) -> number * 2;
    Function<Integer, Integer> functionCompose = plus10.compose(multiply2);
    Function<Integer, Integer> functionAndThen = plus10.andThen(multiply2);

    Assertions.assertThat(plus10.apply(2)).isEqualTo(12);
    Assertions.assertThat(multiply2.apply(2)).isEqualTo(4);
    Assertions.assertThat(functionCompose.apply(2)).isEqualTo(14);
    Assertions.assertThat(functionAndThen.apply(2)).isEqualTo(24);
}
 

Function<T, R>

T타입을 받아서 R타입을 리턴하는 함수 인터페이스입니다.

  • R apply(T t)
  • 함수 조합용 메소드
    • andThen
    • compose
@Test
public void biFunctionTest(){
    Function<Integer, Integer> plus10 = (number) -> number + 10;
    BiFunction<Integer, Integer, Integer> biFunctionSum = (number, number2) -> number + number2;
    BiFunction<Integer, Integer, Integer> biFunctionAndThen = biFunctionSum.andThen(plus10);

    Assertions.assertThat(biFunctionSum.apply(4,8)).isEqualTo(12);
    Assertions.assertThat(biFunctionAndThen.apply(4,8)).isEqualTo(22);
}

BiFunction<T, U, R>

두 개의 값(T, U)을 받아서 R 타입을 리턴하는 함수 인터페이스입니다.

  • R apply(T t, U u)
  • 함수 조합용 메소드
    • andThen
@Test
public void consumerTest(){
    ArrayList<Integer> list = new ArrayList<>();

    Consumer<Integer> addList = (age) -> list.add(age);
    Consumer<Integer> addListPlus10 = (name) -> list.add(name + 10 );
    Consumer<Integer> consumerAndThen = addList.andThen(addListPlus10);

    addList.accept(30);
    Assertions.assertThat(list.size()).isEqualTo(1);
    Assertions.assertThat(list.get(0)).isEqualTo(30);

    list.clear();
    consumerAndThen.accept(30);
    Assertions.assertThat(list.size()).isEqualTo(2);
    Assertions.assertThat(list.get(0)).isEqualTo(30);
    Assertions.assertThat(list.get(1)).isEqualTo(40);
}

Consumer<T>

T타입을 받아서 아무 값도 리턴하지 않는 함수 인터페이스입니다.

  • void accept(T t)
  • 함수 조합용 메소드
    • andThen
@Test
public void supplierTest(){
    Supplier<Integer> getAge = () -> 30;
    Assertions.assertThat(getAge.get()).isEqualTo(30);
}

 

Supplier<T>
T타입의 값을 제공하는 함수 인터페이스입니다.
  • T get()
@Test
public void predicateTest(){
    Predicate<Integer> isEven = (number) -> number % 2 == 0;
    Predicate<Integer> isTen = (number) -> number.equals(10);

    Assertions.assertThat(isEven.test(10)).isEqualTo(true);
    Assertions.assertThat(isEven.and(isTen).test(10)).isEqualTo(true);
    Assertions.assertThat(isEven.or(isTen).test(4)).isEqualTo(true);
    Assertions.assertThat(isEven.negate().test(2)).isEqualTo(false);
}

Predicate<T>

T타입을 받아서 boolean을 리턴하는 함수 인터페이스입니다.

  • boolean test(T t)
  • 함수 조합용 메소드
    • and
    • or
    • negate
@Test
public void unaryOperator(){
    UnaryOperator<Integer> plusTen = (number) -> number + 10;
    Assertions.assertThat(plusTen.apply(10)).isEqualTo(20);
}

UnaryOperator<T>

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

  • R apply(T t)
  • 함수 조합용 메소드
    • andThen
    • compose
@Test
public void BinaryOpreator(){
    BinaryOperator<Integer> binarySum = (number, number2) -> number + number2;
    Assertions.assertThat(binarySum.apply(5,8)).isEqualTo(13);
}

BinaryOperaor<T>

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

  • R apply(T t, U u)
  • 함수 조합용 메소드
    • andThen

 

끝으로 자바에서 함수형 프로그래밍 대해 살펴보겠습니다.

순수 함수(Pure function)란?

함수 밖에 있는 값을 변경하거나 사용하지 않고, 동일한 입력에 대하여 항상 동일한 출력을 반환하는 함수입니다.

  • Side Effect가 없다. (함수 밖에 있는 값을 변경하지 않습니다.)
  • 상태가 없다. (함수 밖에 있는 값을 사용하지 않습니다.)

고차 함수(Higher-Order Function)란?

함수가 함수를 매개변수로 받을 수 있고 함수를 리턴할 수도 있습니다.

 

 

 

이로써 공부한 내용을 간략히 정리해보았습니다. 

감사합니다.


출처 

https://www.inflearn.com/course/the-java-java8/dashboard

'Java' 카테고리의 다른 글

Java8 Optional API  (0) 2022.05.06
Java8 Stream API  (0) 2022.05.04
자바 인터페이스(Interface)  (0) 2022.04.13
애노테이션 프로세서(Annotation processor)  (0) 2022.04.06
프록시 패턴(Proxy Pattern)  (0) 2022.04.05