본문 바로가기
개발 언어/Java

Java8 함수형 인터페이스 Function, Consumer, Predicate, Supplier

by 정권이 내 2023. 11. 15.

Java8 함수형 인터페이스 Function, Consumer, Predicate, Supplier 예제

 

바로 가기

 

 

함수형 인터페이스 개념

주요 특징

  • 단 하나의 추상메서드(SAM: Single Abstract Method)만 가지는데 이 메서드가 함수형 인터페이스의 주요 동작을 결정합니다.
  • 람다 표현식으로 표현 가능합니다.

 

Java8에서 추가된 함수형 인터페이스에는 기본적으로 제공하는 함수형 인터페이스인 Function, Consumer, Predicate, Supplier 등이 있습니다. 각 함수형 인터페이스의 역할은 다음과 같습니다.

  • Consumer<T>: T 타입의 인수를 받아 아무것도 반환하지 않습니다.
  • Function<T, R>: T 타입의 인수를 받아 R 타입의 결과를 반환합니다.
  • Predicate<T>: T 타입의 인수를 받아 true 또는 false를 반환합니다.
  • Supplier<T>: 제네릭 타입 T값을 반환하는 인터페이스 입니다. 지연로딩, 비동기로직에서 주로 사용합니다.

 

Function<T, R>

Function<T,R> 은 제네릭 타입 T를 받아서 제네릭 타입 R로 반환하는 R apply(T t) 추상메서드가 있는 인터페이스 입니다.

 

Function<T, R> 인터페이스

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

 

apply() 사용 예제

Function 인터페이스 toInt 에서 추상메서드를 재정의하여 입력타입 String을 Integer 형태로 출력하는 예제입니다.

public class FunctionInterfaceEx {
    
        public static void main(String[] args) {
    
            Function<String, Integer> toInt = new Function<String, Integer>() {
                @Override
                public Integer apply(String s) {
                    return Integer.parseInt(s);
                }
            };
    
            final Integer number = toInt.apply("100");
            System.out.println("number = " + number);
        }
    }

 

람다표현식을 사용하여 좀더 간단하게 표현할수 있습니다.

public class FunctionInterfaceEx {
    
        public static void main(String[] args) {
    
            Function<String, Integer> toInt = Integer::parseInt;
    
            final Integer number = toInt.apply("100");
            System.out.println("number = " + number);
        }
    }

 

identity() 사용 예제

Function 인터페이스의 identity 메서드는 입력값을 그대로 반환하는 메서드 입니다. 사용목적은 주로 다음과 같습니다.

 

디폴트 매핑 로직 구현

  • 어떤 경우에는 함수형 인터페이스를 사용하는데 있어서 특별한 매핑이 필요하지 않을 때가 있습니다. 이때 identity 함수를 사용하면 디폴트로 아무런 변환 없이 값을 그대로 반환할 수 있습니다.

조합성과 함께 사용

  • identity 함수는 함수 조합(composition)에서 유용하게 사용됩니다. 예를 들어, 함수를 조합할 때 항등원(원소가 자기 자신과 합성해도 변하지 않는 원소)으로서 identity 함수를 활용하여 조합성을 유지할 수 있습니다.
public class FunctionInterfaceEx {
    
        public static void main(String[] args) {
    
            Function<Integer, Integer> identity = Function.identity();
            System.out.println("apply = " + identity.apply(100));
        }
    }

 

 

Consumer<T>

Consumer<T>는 제네릭 타입 입력값만 있고 출력은 없는 void accept(T t) 추상메서드가 있는 인터페이스 입니다.

출력형태가 없으므로 내부에서 값을 출력하거나 데이터를 변형 또는 저장하는 용도로 사용됩니다.

 

Consumer<T> 인터페이스

@FunctionalInterface
    public interface Consumer<T> {
    
        void accept(T t);
    
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }
  • accept(T t): 입력 값을 소비합니다.
  • andThen(Consumer<? super T> after): 주어진 Consumer와 순서대로 소비합니다.

 

accept() 사용 예제

print는 입력값을 그대로 출력하는 Consumer 인터페이스를 구현한 객체입니다. printUpperCase는 입력값을 대문자로 변환하여 출력하는 Consumer 인터페이스를 구현한 객체입니다.

public class ConsumerInterfaceEx {
    
        public static void main(String[] args) {
            final Consumer<String> print = x -> System.out.println(x);
            final Consumer<String> printUpperCase = v -> System.out.println(v.toUpperCase());
            print.accept("Hello World");
            printUpperCase.accept("Hello World");
        }
    }
Hello World
    HELLO WORLD

 

andThen() 사용 예제

consumer1과 consumer2는 각각 입력값을 출력하는 Consumer 인터페이스를 구현한 객체입니다.

consumer3은 consumer1, consumer2를 andThen으로 연결하여 순서대로 출력하는 Consumer 인터페이스입니다.

public class ConsumerInterfaceEx {
    
        public static void main(String[] args) {
            final Consumer<String> print = x -> System.out.println(x);
            final Consumer<String> printUpperCase = v -> System.out.println(v.toUpperCase());
            print.accept("Hello World");
            printUpperCase.accept("Hello World");
    
            final Consumer<String> consumer1 = x -> System.out.println("consumer1: " + x);
            final Consumer<String> consumer2 = x -> System.out.println("consumer2: " + x);
    
            final Consumer<String> consumer3 = consumer1.andThen(consumer2);
            consumer3.accept("Hello World");
        }
    }

 

Predicate<T>

Predicate<T>는 제네릭 타입 입력값을 받고 boolean 타입으로 반환하는 boolean test(T t) 추상메서드가 있는 인터페이스 입니다.

조건을 기반으로 true, false를 판단하여 필터링, 검증, 컬렉션 요소 검사등의 작업에 사용합니다.

 

Predicate<T> 인터페이스

@FunctionalInterface
    public interface Predicate<T> {
    
        boolean test(T t);
    
        default Predicate<T> and(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) && other.test(t);
        }
    
        default Predicate<T> negate() {
            return (t) -> !test(t);
        }
    
        default Predicate<T> or(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) || other.test(t);
        }
    
        static <T> Predicate<T> isEqual(Object targetRef) {
            return (null == targetRef)
                    ? Objects::isNull
                    : object -> targetRef.equals(object);
        }
    
        @SuppressWarnings("unchecked")
        static <T> Predicate<T> not(Predicate<? super T> target) {
            Objects.requireNonNull(target);
            return (Predicate<T>)target.negate();
        }
    }
  • test(T t): 입력 값이 주어진 조건을 만족하는지 여부를 확인하여 boolean 값을 반환합니다.
  • and(Predicate<? super T> other): 주어진 Predicate와 조건을 결합하여 새로운 Predicate를 반환합니다.
  • or(Predicate<? super T> other): 주어진 Predicate와 조건을 결합하여 새로운 Predicate를 반환합니다.
  • negate(): 주어진 Predicate의 부정을 반환합니다.
  • isEqual(Object obj): 주어진 객체와 같은지 여부를 확인하여 boolean 값을 반환합니다.

 

test() 사용 예제

public class PredicateInterfaceEx {
    
        public static void main(String[] args) {
    
            //Predicate test method
            final Predicate<Integer> isOddNumber = (number) -> number % 2 == 1;
            System.out.println("isOddNumber.test(3): " + isOddNumber.test(3));
            System.out.println("isOddNumber.test(4): " + isOddNumber.test(4));
            System.out.println("isOddNumber.test(5): " + isOddNumber.test(5));
        }
    }
isOddNumber.test(3): true
    isOddNumber.test(4): false
    isOddNumber.test(5): true

isOddNumber.test(T t): 입력값이 홀수인지 여부를 확인하는 Predicate 인터페이스를 구현한 객체입니다.

 

and() 사용 예제

홀수여부를 체크하는 isOddNumber와 10보다 큰수를 체크하는 isBigger 인터페이스를 and로 결합한 예제 입니다.

public class PredicateInterfaceEx {
    
        public static void main(String[] args) {
    
            //Predicate test method
            final Predicate<Integer> isOddNumber = (number) -> number % 2 == 1;
    
            //Predicate and method
            final Predicate<Integer> isBigger = (number) -> number > 10;
            System.out.println("isOddNumber.and(isBigger).test(9): " + isOddNumber.and(isBigger).test(9));
            System.out.println("isOddNumber.and(isBigger).test(10): " + isOddNumber.and(isBigger).test(10));
            System.out.println("isOddNumber.and(isBigger).test(11): " + isOddNumber.and(isBigger).test(11));
        }
    }
isOddNumber.and(isBigger).test(9): false
    isOddNumber.and(isBigger).test(10): false
    isOddNumber.and(isBigger).test(11): true

 

or() 사용 예제

입력값의 홀수여부를 반환하는 isOddNumber와 5보다 작은값인지에 대해 반환하는 isSmaller 인터페이스를 or로 결합한 예제 입니다.

public class PredicateInterfaceEx {
    
        public static void main(String[] args) {
    
            //Predicate test method
            final Predicate<Integer> isOddNumber = (number) -> number % 2 == 1;
    
            //Predicate or method
            final Predicate<Integer> isSmaller = (number) -> number < 5;
            System.out.println("isOddNumber.or(isSmaller).test(4): " + isOddNumber.or(isSmaller).test(4));
            System.out.println("isOddNumber.or(isSmaller).test(5): " + isOddNumber.or(isSmaller).test(5));
            System.out.println("isOddNumber.or(isSmaller).test(6): " + isOddNumber.or(isSmaller).test(6));
            System.out.println("================================================");
        }
    }
isOddNumber.or(isSmaller).test(4): true
    isOddNumber.or(isSmaller).test(5): true
    isOddNumber.or(isSmaller).test(6): false

 

negate() 사용 예제

입력값의 홀수여부에 대해 부정의 결과를 출력하는 에제입니다.

public class PredicateInterfaceEx {
    
        public static void main(String[] args) {
    
            //Predicate negate method
            System.out.println("isOddNumber.negate().test(3): " + isOddNumber.negate().test(3));
            System.out.println("isOddNumber.negate().test(4): " + isOddNumber.negate().test(4));
            System.out.println("isOddNumber.negate().test(5): " + isOddNumber.negate().test(5));
        }
    }
isOddNumber.negate().test(3): false
    isOddNumber.negate().test(4): true
    isOddNumber.negate().test(5): false

 

isEqual() 사용 예제

입력값이 "Hello"인지에 대해 여부를 확인하는 예제입니다.

public class PredicateInterfaceEx {
    
        public static void main(String[] args) {
    
            //Predicate isEqual method
            final Predicate<String> isHello = Predicate.isEqual("Hello");
            System.out.println("isHello.test(\"Hello\"): " + isHello.test("Hello"));
            System.out.println("isHello.test(\"World\"): " + isHello.test("World"));
        }
    }
isHello.test("Hello"): true
    isHello.test("World"): false

 

 

Supplier<T>

java8 기본 함수형 인터페이스 Supplier<T>는 제네릭 타입값을 반환하는 T get() 추상메서드가 있는 인터페이스 입니다.

객체 생성이나 초기화에 부하가 큰 경우 지연 로딩(lazy loading)이나 비동기작업에서 유용하게 쓸수 있습니다.

 

Supplier<T> 인터페이스

@FunctionalInterface
    public interface Supplier<T> {
    
        T get();
    }

 

get() 사용 예제

lazyLoading(): 실행시 1초의 sleep을 수행하고 "HelloWorld"를 반환하는 메서드입니다.

print(): int, String 타입을 인자로 받고 int 타입의 변수가 5 초과인 경우에만 문자열을 출력하는 메서드입니다.

이때 print() 메서드를 호출시 첫번째 인자값이 4, 5일때 number > 5 조건이 성립하지 않기 때문에 print() 메서드의 출력문이 실행되지 않지만 lazyLoading() 메서드가 두번째 인자값에서 호출되면서 1초씩 지연이 발생합니다.

public class SupplierInterfaceEx {
    
        public static void main(String[] args) {
    
            long start = System.currentTimeMillis();
            print(4, lazyLoading());
            print(5, lazyLoading());
            print(6, lazyLoading());
            System.out.println("lazy time: " + ((System.currentTimeMillis() - start) / 1000) + " seconds");
    
        }
    
        private static String lazyLoading() {
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("InterruptedException: " + e.getMessage());
            }
    
            return "Hello World";
        }
    
        private static void print(int number, String value) {
            if (number > 5) {
                System.out.printf("%d is bigger than 5, %s\n", number, value);
            }
        }
    
    }
6 is bigger than 5, Hello World
    lazy time: 3 seconds

 

이때 lazyLoading() 메서드를 Supplier 인터페이스 get() 추상메서드의 출력값으로 하면 get() 메서드를 호출할때만 lazyLoading() 메서드가 실행되기 때문에 print() 메서드 호출시 첫번째 인자값이 조건에 안맞는 경우는 지연시간 없이 수행할수 있습니다.

public class SupplierInterfaceEx {
    
        public static void main(String[] args) {
    
            long start = System.currentTimeMillis();
            print(4, SupplierInterfaceEx::lazyLoading);
            print(5, SupplierInterfaceEx::lazyLoading);
            print(6, SupplierInterfaceEx::lazyLoading);
            System.out.println("lazy time: " + ((System.currentTimeMillis() - start) / 1000) + " seconds");
    
        }
    
        private static String lazyLoading() {
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("InterruptedException: " + e.getMessage());
            }
    
            return "Hello World";
        }
    
        private static void print(int number, Supplier<String> value) {
            if (number > 5) {
                System.out.printf("%d is bigger than 5, %s\n", number, value.get());
            }
        }
    
    }
6 is bigger than 5, Hello World
    lazy time: 1 seconds

 

내용 정리

  • Function<T, R>: T 타입의 인수를 받아 R 타입의 결과를 반환합니다.
  • Consumer<T>: T 타입의 인수를 받아 아무것도 반환하지 않습니다.
  • Predicate<T>: T 타입의 인수를 받아 true 또는 false를 반환합니다.
  • Supplier<T>: 제네릭 타입 T값을 반환하는 인터페이스 입니다. 지연로딩, 비동기로직에서 주로 사용합니다.
반응형

댓글