BackEnd/Java

[Java] 함수형 인터페이스란?

꾹꾹이 2022. 10. 2.
728x90

함수형 인터페이스란?

  • 추상 메서드를 하나만 갖는 인터페이스
  • @FuncationInterface 어노테이션을 갖는 인터페이스
  • Single Abstract Method(SAM) 인터페이스

 

예시

1
2
3
public interface FunctionalInterface {
    public abstract void doSomething();
}
cs

 

 

 

함수형 인터페이스를 사용하는 이유

자바의 람다식을 사용할 때 함수형 인터페이스로만 접근이 가능하기 때문이다.

@FuncationInterface 어노테이션을 붙여주면 함수형 인터페이스의 규칙을 위반했을 때 에러를 발생시킨다.

 

기존 방식

1
2
3
4
5
6
7
8
FunctionalInterface func = new FunctionalInterface() {
    @Override
    public void doSomething() {
        System.out.println(text);
    }
};
 
func.doSomething("do something");
cs

 

 

람다식을 사용한 경우

1
2
3
4
5
public interface FunctionalInterface {
     public abstract void doSomething();
}
 
FunctionalInterface func = () -> System.out.println("do something");
func.doSomething();
cs

 

 

기본 함수형 인터페이스

자바에서 기본적으로 제공하는 함수형 인터페이스이다. 기본적으로 제공되는 것만 사용해도 웬만한 람다식은 다 만들 수 있다.

따라서 개발자는 직접 함수형 인터페이스를 만드는 경우가 거의 없다.

  • Runnable
  • Supplier
  • Consumer
  • Function<T,R>
  • Predicate

 

Runnable

인자를 받지 않고 리턴값도 없는 인터페이스이다. run 메소드를 사용한다.

1
2
3
4
5
6
public interface Runnable {
    public abstract void run();
}

-----------------------------------------------------

Runnable runnable = () -> System.out.println("run~");
runnable.run();    //결과: run~
cs

 

Supplier

인자를 받지 않고 T 타입의 객체를 리턴한다. get 메소드를 사용한다.

1
2
3
4
5
6
7
public interface Supplier<T> {
    T get();
}

-----------------------------------------------------

Supplier<String> getString = () -> "supplier test!";
String str = getString.get();
System.out.println(str);    //결과: supplier test!
cs

 

Consumer

T 타입의 객체를 인자로 받고 리턴 값은 없다. appept 메소드를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
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); };
    }
}

-----------------------------------------------------

Consumer<String> printString = text -> System.out.println("Consumrer is " + text );
printString.accept("you");    //결과: Consumer is you
cs

andThen 메소드를 사용하면 두개 이상의 Consumer를 연속 실행할 수 있다.

1
2
3
4
Consumer<String> printString = text -> System.out.println("By hello" + text );
Consumer<String> printString2 = text -> System.out.println("!!");
 
printString.andThen(printString2).accept("world");    //결과: By hello world !!
cs

 

Function

T 타입의 인자를 받고, R 타입의 객체를 리턴한다. apply 메소드를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface Function<T, R> {
    R apply(T, t);
 
    default <V> Function<V, R> compose(Function<super V, ? extends T> before) {        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
 
    default <V> Function<T, V> andThen(Function<super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
 
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
 
-----------------------------------------------------
 
 
Function<Integer, Integer> multiply = (value) -> value *2;
Integer result = nultiply.apply(3);
System.out.println(result);    //결과: 6
cs

 

compose 메소드를 사용하면 Function을 조합하여 사용할 수 있다. 

단, andThen 메소드와 실행 순서가 반대이다. compose()에 인자로 전달되는 Function이 먼저 실행되고 이후에 호출하는 객체의 Function이 실행된다.

1
2
3
4
5
6
7
Function<Integer, Integer> multiply = (value) -> value * 2;
Function<Integer, Integer> add      = (value) -> value + 5;
 
Function<Integer, Integer> addThenMultiply = multiply.compose(add);
 
Integer result1 = addThenMultiply.apply(3);
System.out.println(result1);    // 결과: 16
cs

add함수가 먼저 실행되고 multiply 함수가 실행된다.

 

Predicate

T 타입 인자를 받고 결과로 boolean을 리턴한다. test 메소드를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);
    }
}
 
-----------------------------------------------------
 
Predicate<Integer> equalThanTen = num -> num == 10;
System.out.println(equalThenTen.test(10));    //결과: true
cs

 

and 메소드와 or 메소드를 함께 사용할 수 있다.

and 메소듣는 두개의 Predicate가 true 일 때 참, or 메소드는 두개 중 하나만 true이면 참이다.

isEquals 메소드는 인자로 전달되는 객체와 같은지 체크해준다. 

1
2
3
4
5
6
7
Predicate<Integer> biggerThanTen = num -> num > 10;
Predicate<Integer> lowerThanTen = num -> num < 10;
Predicate<Integer> equals = Predicate.isEqual(10);
 
System.out.println(biggerThanTen.and(lowerThanTen.test(5));        //결과: false
System.out.println(lowerThanThen.or(lowerThanTen.test(5));        //결과: true
isEquals.test(10);   //결과: true
 
cs

 

'BackEnd > Java' 카테고리의 다른 글

[Java] 싱글톤(Singleton)이란?  (0) 2022.10.10
[Java] 메소드 레퍼런스란?  (0) 2022.10.09
[JAVA] LinkedList의 개념 및 사용법  (1) 2022.09.07
[JAVA] for문 리팩토링하기(List)  (0) 2022.09.04
[JAVA] 생성자 정리  (0) 2022.09.04

댓글