시뻘건 개발 도전기

Functional Interface 본문

프로그래밍/JAVA

Functional Interface

시뻘건볼때기 2022. 7. 6. 16:48
반응형

더 많은 Functional Interface를 살펴보자.

 

첫 번째로 매개변수가 없는 T 타입의 값을 리턴하는 "Supplier"이다.

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
public class Exercise {
    public static void main(String[] args) {
        Supplier<Double> myEx = () -> Math.random();
        System.out.println(myEx.get());
        System.out.println(myEx.get());
        System.out.println(myEx.get());
    }
}

 

위와 같이 Supplier는 기본적으로 사용할 수 있지만, 아래와 같이 유연하게 여러 종류의 Supplier를 공급할 수 있다.

public class Exercise {
    public static void main(String[] args) {
        Supplier<Double> myEx = () -> Math.random();
        printHelper(myEx, 5);
    }

    public static void printHelper(Supplier<Double> supplier, int count) {
        for(int i = 0 ; i < count ; i++) {
            System.out.println(supplier.get() + "입니다.");
        }
    }
}

 

 

두 번째는 T 타입 매개변수를 받고 리턴하지 않는 "Cunsumer"이다.

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    
    // ...

}
public class Exercise {
    public static void main(String[] args) {
        Consumer<String> myEx = p1 -> System.out.println(p1 + "어셈블");
        myEx.accept("어벤져스 ");   // 어벤져스 어셈블
    }
}

Consumer도 마찬가지로 아래와 같이 유연하게 사용할 수 있다.

제네릭 타입을 사용하게되면 더욱 좋은 확장성을 자랑할 수 있다.

public class Exercise {
    public static void main(String[] args) {
        Consumer<String> myEx = p1 -> System.out.println(p1 + " - 100kg.");
        List<String> list = Arrays.asList("스쿼트", "벤치프레스", "데드리프트");
        processHelper(list, myEx);
        /*
            스쿼트 - 100kg.
            벤치프레스 - 100kg.
            데드리프트 - 100kg.
         */
    }

    public static void processHelper(List<String> list, Consumer<String> consumer) {
        for(String param : list) {
            consumer.accept(param);
        }
    }
}
public class Exercise {
    public static void main(String[] args) {
        Consumer<String> myEx = p1 -> System.out.println(p1 + " - 100kg.");
        List<String> list = Arrays.asList("스쿼트", "벤치프레스", "데드리프트");
        processHelper(list, myEx);
        /*
            스쿼트 - 100kg.
            벤치프레스 - 100kg.
            데드리프트 - 100kg.
         */

        Consumer<Integer> myEx2 = p1 -> System.out.println("삼대 " + p1);
        List<Integer> list2 = Arrays.asList(300, 400, 500);
        processHelper(list2, myEx2);
        /*
            삼대 300
            삼대 400
            삼대 500
         */
    }

    public static <T> void processHelper(List<T> list, Consumer<T> consumer) {
        for(T param : list) {
            consumer.accept(param);
        }
    }
}

 

세 번째로는 T 타입과 U 타입의 매개변수를 받고 리턴하지 않는"BiConsumer"이다.

@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);
    
    // ...
    
}
public class Exercise {
    public static void main(String[] args) {
        BiConsumer<Integer, String> myEx = (p1, p2) -> System.out.println(p2 + p1);
        List<Integer> list = Arrays.asList(1, 2, 3);
        processHelper(list, myEx);
        /*
            매개변수는 1
            매개변수는 2
            매개변수는 3
         */
    }

    public static <T> void processHelper(List<T> list, BiConsumer<T, String> biConsumer) {
        for(T param : list) {
            biConsumer.accept(param, "매개변수는 ");
        }
    }
}

 

네 번째는 T타입의 매개변수를 받아서 boolean을 리턴하는 "Predicate"이다.

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);

	// 내가 만든 predicate과 매개변수로 들어온 predicate 둘 다 만족하는지?
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

	// 내가 만든 predicate과 반대 역할
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

	// 내가 만든 predicate과 매개변수로 들어온 predicate중 하나라도 만족하는지?
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    // ...
    
}
public class Exercise {
    public static void main(String[] args) {
        Predicate<Integer> myEx = p1 -> p1 > 0;
        List<Integer> list = Arrays.asList(1, 2, 3, -3, -5, 0);

        List<Integer> result = filter(list, myEx);
        System.out.println(result); // [1, 2, 3]

        List<Integer> result2 = filter(list, myEx.negate());
        System.out.println(result2); // [-3, -5, 0]

        List<Integer> result3 = filter(list, myEx.or(p1 -> p1 == 0));
        System.out.println(result3); // [1, 2, 3, 0]

        List<Integer> result4 = filter(list, myEx.and(p1 -> p1%2 == 1));
        System.out.println(result4); // [1, 3]
    }

    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();

        for(T param : list) {
            if(predicate.test(param)) {
                result.add(param);
            }
        }

        return result;
    }
}

 

 

눈치가 빠른 사람은 벌써 알 수도 있다.

"첫 시간에 참고했던 java 1.8 docs에서 살펴본 수 많은 function을 다 살펴볼 필요가 없겠구나!!!!!"

해당 docs를 잘 살펴보면 알겠지만 Function 류와 Predecate 류, Supplier 류, Consumer 류, Operator 류가 있다.

여기서 앞에 "Bi"가 붙으면 매개변수가 두 개이고, 타입이 prefix로 붙는 녀석은 그 타입을 받는 다는 거구나!!!!!

그렇다면 우리는 이제 인터페이스 명만 보아도 언제 사용할 수 있을지 유추할 수 있다.

다만 Operator의 경우에는 매개변수의 타입과 리턴되는 값의 타입이 같으면 Operator를 사용한다.

 

여기서 이런 의문이 들 수도 있다.

Predicate<T>를 사용해서 유연하게 제네릭 타입을 사용할 수 있는데
굳이 왜 IntPredicate이 있는걸까?

 

맞다. Predicate 뿐만 아니라 대부분 제네릭 타입을 제공하기 때문에 굳이 타입이 지정되어 있는 이유는 메모리다.

int를 boxing하면 Interger, Integer를 unboxing하면 int이면서 메모리 차이도 있다. 이러한 차원에서 메모리를 아끼기 위해 IntPredicate처럼 타입이 지정되어 제공되기도 한다.

@FunctionalInterface
public interface IntPredicate {
    boolean test(int value);
}

 

 

마지막으로 T 타입의 매개변수를 받아 int형 값을 리턴하는 "Comparator"이다.

이 인터페이스는 java.util 안에 있으면서 종종 정렬에 사용되는 녀석인데 코딩테스트때 자주 등장한다.

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    
    // ...
    
}

 

Comparator는 어떠한 객체끼리 정렬할 때 사용되는데, 리턴되는 값과 그 기준은 아래와 같다.

  • 리턴 값이 음수 : o1 < o2
  • 리턴 값이 0 : o1 = o2
  • 리턴값이 양수 : o1 > o2
public class Exercise {
    public static void main(String[] args) {
        User u1 = new User(1, "일인자");
        User u2 = new User(2, "이인자");
        User u3 = new User(3, "삼인자");

        List<User> users = Arrays.asList(u1, u2, u3);
        Comparator<User> userComparator = (p1, p2) -> p1.getName().compareTo(p2.getName());

        System.out.println(users);  // [User(id=1, name=일인자), User(id=2, name=이인자), User(id=3, name=삼인자)]
        Collections.sort(users, userComparator);
        System.out.println(users);  // [User(id=3, name=삼인자), User(id=2, name=이인자), User(id=1, name=일인자)]
    }
}

@AllArgsConstructor
@Getter
@ToString
class User {
    private int id;
    private String name;
}
반응형

'프로그래밍 > JAVA' 카테고리의 다른 글

Optional  (0) 2022.08.12
Stream  (0) 2022.07.25
Method Reference  (0) 2022.07.14
Lambda Expression  (0) 2022.07.06
함수형 프로그래밍  (0) 2022.07.06
Comments