본문 바로가기
자바

[Java] 표준 API의 함수형 인터페이스, java.util.function 패키지

by 책 읽는 개발자_테드 2021. 3. 7.
반응형

이 글은 java.util.function 패키지의 함수형 인터페이스 표준 API에 대해서 설명합니다.

학습 목표

· java.util.function 패키지

· Consumer

· Supplier

· Function

· Operator

· Predicate


java.util.function 패키지

자바에서 제공되는 표준API에서 한 개의 추상 메소드를 가지는 인터페이스들은 모두 람다식을 이용해서 익명 객체로 표현 가능하다.

 

예를 들어 스레드의 작업을 정의하는 Runnable 인터페이스는 매개 변수와 리턴값이 없는 run() 메소드만 존재하므로 람다식을 이용해서 Runnable 인터페이스를 생성할 수 있다.

 

Runnable runnable = ()-> {
System.out.println("스레드 시작");
};

Thread thread = new Thread(runnable);
thread.run();

 

Thread 생성자를 호출할 때 람다식을 매개값으로 대입해도 된다.

 

Thread thread = new Thread(() ->{ System.out.println("스레드 시작");});
thread.run();

 

자바 8부터는 빈번하게 사용되는 함수형 인터페이스java.util.function 표준 API 패키지로 제공한다.

 

java.util.function 패키지의 함수형 인터페이스는 크게 Consumer, Supplier, Function, Operator, Predicate로 구분된다. 구분 기준은 인터페이스에 선언된 추상 메소드의 매개값과 리턴값의 유무다.

 

종류

추상메소드 특징

Consumer

매개값 o, 리턴값 x

Supplier

매개값 x, 리턴값 o

Function

매개값 o, 리턴값 o
주로 매개값을 리턴값으로 매핑(타입 변환)

Operator

매개값 o, 리턴값 o
주로 매개값을 연산하고 결과를 리턴

Predicate

매개값 o, 리턴값 o (boolean 타입으로 한정)

 

Consumer

Consumer 함수형 인터페이스은 리턴값이 없는 accept() 메소드를 갖는다. accept()는 단지 매개값을 소비하는 역할을 하고, 리턴 값이 없다.

 

매개 변수의 타입과 수에 따라 아래와 같은 Consumer들이 있다.

인터페이스명

추상메소드

설명

Consumer<T>

void accept(T t)

객체 T를 받아 소비

BiConsumer<T>

void accept(T t, U u)

객체 T와 U를 받아 소비

DoubleConsumer<T>

void accept(double value)

double 값을 받아 소비

IntConsumer<T>

void accept(int value)

int 값을 받아 소비

LongConsumer<T>

void accept(long value)

long 값을 받아 소비

ObjDoubleConsumer<T>

void accept(T t, double value)

객체 T와 double 값을 받아 소비

ObjIntConsumer<T>

void accept(T t, int value)

객체 T와 int 값을 받아 소비

ObjLongConsumer<T>

void accept(T t, long value)

객체 T와 long 값을 받아 소비

 

예시

public class ConsumerExam {
    public static void main(String []args){
        Consumer<String> consumer = t -> System.out.println(t + "8");
        consumer.accept("Java");

        BiConsumer<String, String> bigConsumer = (t, u) -> System.out.println(t+u);
        bigConsumer.accept("Java", "8");

        DoubleConsumer doubleConsumer = d -> System.out.println("Java" + d);
        doubleConsumer.accept(8.0);

        ObjIntConsumer<String> objIntConsumer = (t, i) -> System.out.println(t + i);
        objIntConsumer.accept("Java",8);
    }
}

 

 

Supplier

Supplier 함수형 인터페이스는 매개 값이 없고 리턴값이 있는 getXXX() 메소드를 갖는다. 이 메소드는 실행 후 호출한 곳으로 데이터를 공급하는 역할을 한다.

 

리턴 타입에 따라서 아래와 같은 Supplier 함수형 인터페이스가 있다.

인터페이스명

추상메소드

설명

Supplier<T>

T get()

T 객체를 리턴

BooleanSupplier

boolean getAsBoolean()

boolean 값을 리턴

DoubleSupplier

double getAsDouble()

double 값을 리턴

IntSupplier

Int getAsInt()

int 값을 리턴

LongSupplier

long getAsLong()

long 값을 리턴

예시 

public class SupplierExam {
    public static void main(String []args){
        IntSupplier intSupplier = () -> {
            int num = (int) (Math.random() * 6) + 1;
            return num;
        };
        
        int num = intSupplier.getAsInt();
        System.out.println("눈의 수: " + num);
    }
}

 

Function

 

Function 함수형 인터페이스는 매개값과 리턴값이 있는 applyXXX() 메소드를 갖는다. 이 메소드들은 매개값을 리턴값으로 매핑(타입 변환)하는 역할을 한다.

 

매개 변수 타입과 리턴 타입에 따라서 아래와 같은 Function 함수형 인터페이스가 있다.

인터페이스명

추상메소드

설명

Function<T,R>

R apply(T t)

객체 T를 객체 R로 매핑

BiFunction<T,U,R>

R apply(T t, U u)

객체 T와 U를 객체 R로 매핑

DoubleFunction<R>

R apply(int value)

double을 객체 R로 매핑

IntFunction<R>

R apply(int value)

int를 객체 R로 매핑

IntToDoubleFunction

double applyAsDouble(int value)

int를 double로 매핑

IntToLongFunction<R>

long applyAsLong(int value)

int를 long으로 매핑

LongToDoubleFunction<R>

double apply(T t)

long을 double로 매핑

LongToIntFunction<R>

int applyAsInt(long value)

long을 int로 매핑

ToDoubleBiFunction<T,U>

double applyAsDouble(T t, U u)

객체 T와 U를 double로 매핑

ToDoubleFunction<T>

double applyAsDouble(T vaule)

객체 T를 double로 매핑

ToIntBiFunction<T,U>

int applyAsInt(T t, U u)

객체 T와 U를 Int로 매핑

ToIntFunction<T>

int applyAsInt(T value)

객체 T를 Int로 매핑

ToLongBiFunction<T,U>

long applyAsLong(T t, U u)

객체 T와 U를 long으로 매핑

ToLongFunction<T>

long applyAsLong(T value)

객체 T를 long으로 매핑

 

예시

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntFunction;

public class FunctionExam {
    public static void main(String args[]){
        List<Student> list = Arrays.asList(
                new Student("리누스", 90, 96),
                new Student("토발스", 95, 93)
        );

        System.out.println("학생 이름");
        studentStringPrint(list, t -> t.getName());

        System.out.println("영어 점수");
        studentIntPrint(list, t -> t.getEnglishScore());

        System.out.println("수학 점수");
        studentIntPrint(list, t -> t.getMathScore());
    }

    public static void studentIntPrint(List<Student> students,ToIntFunction<Student> function){
        for(Student student: students){
            System.out.print(function.applyAsInt(student) + " ");
        }
        System.out.println();
    }

    public static void studentStringPrint(List<Student> students, Function<Student, String> function){
        for(Student student: students){
            System.out.print(function.apply(student) + " ");
        }
        System.out.println();
    }

}

 

Operator

Operator 함수형 인터페이스는 Function과 동일하게 매개 변수와 리턴값이 있는 applyXXX() 메소드를 갖는다. 

 

하지만 이 메소들은 주로 매개값을 이용해서 연산을 수행한 후 동일한 타입으로 리턴값을 제공하는 역할을 한다는 점에서 Function과 다르다.

 

매개 변수 타입과 수에 따라서 아래와 같은 Operator 함수형 인터페이스가 있다.

인터페이스명

추상메소드

설명

BinaryOperator<T>

BiFunction<T,U,R>

T와 U를 연산한 후 R 리턴

UnaryOperator<T>

Function<T,R>의 하위 인터페이스

T를 연산한 후 R 리턴

DoubleBInaryOperator

double applyAsDouble(double)

두 개의 double 연산

DoubleUnaryOperator

double applyAsDouble(double)

한 개의 double 연산

IntBinaryOperator

int applyAsInt(int, int)

두 개의 int 연산

IntUnaryOperator

int applyAsInt(int)

한 개의 int 연산

LongBinaryOperator

long applyAsLong(long, long)

두 개의 long 연산

LongUnaryOperator

long applyAsLong(long)

한 개의 long 연산

 

예시

import java.util.function.IntBinaryOperator;

public class OperatorExam {
    public static void main(String[] args){
        int[] scores = {92, 95, 87};

        // 최대값 얻기
        int max = maxOrWin(scores,
                (a, b) -> {
                    if(a>=b) return a;
                    else return b;
                });
        System.out.println("최대값: " + max);

        //최소값 얻기
        int min = maxOrWin(scores,
                (a,b) -> {
                    if(a<=b) return a;
                    else return b;
                });
        System.out.println("최소값: " + min);

    }

    public static int maxOrWin(int [] scores, IntBinaryOperator operator){
        int result = scores[0];
        for(int score : scores){
            result = operator.applyAsInt(result, score);
        }
        return result;
    }
}

 

Predicate

Predicate 함수형 인터페이스는 매개 변수와 boolean 리턴값이 있는 testXXX() 메소드를 가지오 있다. 이 메소드들은 매개값을 조사해서 true 또는 false를 리턴한다.

 

매개 변수 타입과 수에 따라서 아래와 같은 Predicate 함수형 인터페이스가 있다.

인터페이스명

추상메소드

설명

Predicate<T>

boolean test(T t)

객체 T를 조사

BiPredicate<T, U>

boolean test(double value)

double 값을 조사

IntPredicate

boolean test(int value)

int 값을 조사

LongPredicate

boolean test(long value)

long 값을 조사

 

예시

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

public class PredicateExam {
    public static void main(String[] args){
        List<Student> students = Arrays.asList(
          new Student("리누스","남자",88),
          new Student("토발즈","남자",95),
          new Student("안나","여자",95),
          new Student("카레니나","여자",92)
        );

        double maleAvg = avg(students, t-> t.getSex().equals("남자"));
        System.out.println("남자 평균 정수: " + maleAvg);

        double femaleAvg = avg(students, t->t.getSex().equals("여자"));
        System.out.println("여자 평균 점수: " + femaleAvg);
    }

    public static double avg(List<Student> students, Predicate<Student> predicate){
        int count = 0, sum = 0;
        for(Student student : students){
            if(predicate.test(student)){
                count ++;
                sum += student.getScore();
            }
        }
        return (double) sum / count;
    }
}

반응형

댓글