Study/Java

자바 기본서를 다시 읽다. 5 - 함수적 인터페이스의 Default Method

going.yoon 2022. 3. 27. 22:54

4강에서 알아보았던 함수적 인터페이스들은 andThen()과 compose()라는 디폴트 메소드를 가지고 있다.

이 디폴트 메소드들은 첫번째 메소드의 처리결과를 두번째 메소드의 매개값으로 넘겨주지만,

인터페이스AB = 인터페이스A.andThen(인터페이스B);

or

인터페이스AB = 인터페이스B.andThen(인터페이스A);

최종결과 = 인터페이스AB.method();

위와같이 인터페이스 AB 의 method()를 호출하기 위해 매개값으로 인터페이스 A와 B의 연산값을 넘겨준다면

andThen인터페이스A 호출 -> 인터페이스B 호출

compose인터페이스B 호출 -> 인터페이스A 호출을 먼저 한다.

 

 

ConsumerFunction API를 사용한 코드를 통해 알아보자.

import java.util.function.Consumer;
import java.util.function.Function;

public class Address {
    private String country;
    private String city;

    public Address(String country, String city){
        this.country = country;
        this.city = city;
    }

    public String getCountry() {
        return this.country;
    }

    public String getCity(){
        return this.city;
    }

}


public class Member {
    private String name;
    private String id;
    private Address address;

    public Member(String name, String id, Address address){
        this.name = name;
        this.id =id;
        this.address = address;
    }

    public String getName() { return this.name ;}
    public String getId() { return this.id;}
    public Address getAddress() { return this.address;}
}


public class ConsumerAndThenExample {
    public static void main(String[] args){

        System.out.println("=========consumer test=========");

        Consumer<Member> consumerA = (m) -> { 
            // #1. 나는 Membertype이 들어오면 name을 리턴해주는 Consumer 표준 API이다.
            System.out.println("consumerA: " + m.getName());
        };

        Consumer<Member> consumerB = (m) -> {
            // #2. 나는 Membertype이 들어오면 id를 리턴해주는 Consumer 표준 API이다.
            System.out.println("consumerB: " + m.getId());
        };

        Consumer<Member> consumerAB = consumerA.andThen(consumerB);
         //#3. member의 name -> id 순서대로 리턴
        consumerAB.accept(new Member("홍길동","hong", null));

        System.out.println("=========function test=========");

        Function<Member, Address> functionA;
        Function<Address, String> functionB;
        Function<Member, String> functionAB;
        String city;
        
        // #4. 나는 Member타입을 매개변수로, Address를 리턴해주는 API이다.
        functionA = (m) -> m.getAddress();
        
        // #5. 나는 Address타입을 매개변수로, String을 리턴해주는 API이다.
        functionB = (a) -> a.getCity();

        functionAB = functionA.andThen(functionB);
        city = functionAB.apply(
                // #6. Member변수를 매개로 -> address -> String 을 리턴받아 City에 저장
                new Member("홍길동", "hong", new Address("한국","서울"))
        );
        System.out.println("거주도시 : " + city);

        functionAB = functionB.compose(functionA);
        city = functionAB.apply(
                // #7. 위에랑 똑같지만, compose를 사용하여 순서를 조정함에 주목.
                new Member("청길동", "chung", new Address("조선","한양"))
        );
        System.out.println("거주도시 : " + city);
    }
}

실행 결과는 다음과 같다.

 

 

다음은 Predicate API의 and, or, negate, isEqual에 대해 알아보겠다. 코드로 알아보자.

 

import java.util.function.IntPredicate;
import java.util.function.Predicate;

public class ConsumerAndThenExample {
    public static void main(String[] args){

        System.out.println("*******and, or, negate test*********");

        // #1. predicate API를 정의
        IntPredicate predicateA = a-> a%2==0;
        IntPredicate predicateB = b-> b%3==0;

        IntPredicate predicateAB ;
        boolean result;

        // #2. A -> B의 순서로 predicateAB의 test 메소드에 인자로 넘겨준다.
        predicateAB = predicateA.and(predicateB);
        result = predicateAB.test(9);
        System.out.println("9는 2와 3의 배수입니까? :" + result);


        predicateAB = predicateA.or(predicateB);
        result = predicateAB.test(9);
        System.out.println("9는 2 또는 3의 배수입니까? : "+ result);

        // #3. negate : 원래 결과값의 반대로 출력
        predicateAB = predicateA.negate();
        result = predicateAB.test(9);
        System.out.println("9는 홀수입니까? " + result);

        System.out.println("*******IsEqual method test*********");

        Predicate<String> predicate;
        // #4. targetObject를 null로 설정
        predicate = Predicate.isEqual(null);

        // #5. sourceObject도 null로 설정 후 test method 호출
        System.out.println("null, null : " + predicate.test(null) );

        predicate = Predicate.isEqual(null);
        System.out.println("null, JAVA8 : " + predicate.test("JAVA8"));

        predicate = Predicate.isEqual("JAVA8");
        System.out.println("JAVA8, JAVA8 : " + predicate.test("JAVA8"));
    }
}

 

출력 결과는 다음과 같다. 

 

 

다음은 BinaryOperator 함수적인터페이스가 제공하는 minBy(), maxBy() 정적 메소드를 알아보자.

이 두 메소드는 매개값으로 제공되는 Comparator를 이용해서 최대 T와 최소 T를 얻는 BinaryOperator를 제공한다.

 

BinaryOperator<T>가 제공하는 정적메소드

  • minBy(Comparator<? Super T> Comparator)
  • maxBy(Comparator<? Super T> Comparator)
// #1. 함수적 인터페이스로 선언된 Comparator 방식
@FunctionalInterface
public interface Comparator<T>{
	public int compare(T o1, T o2);
}

// #2. 람다식으로 선언된 Comparator 방식
(o1, o2) -> {...; return intVal;}

 

 

예시로 알아보자.

import java.util.function.*;

public class ConsumerAndThenExample {
    public static void main(String[] args) {

        BinaryOperator<Fruit> binaryOperator;
        Fruit fruit;
        Fruit 딸기 = new Fruit("딸기", 6000);
        Fruit 수박 = new Fruit("수박" , 10000);

        // #1. 두 객체의 price 값으로 작은 값을 리턴하는 BinaryOperator API 정의
        binaryOperator = BinaryOperator.minBy((f1, f2)-> Integer.compare(f1.price, f2.price));
        fruit = binaryOperator.apply(딸기, 수박);
        System.out.println("minby Operation result : " + fruit.name);

        // #2. 두 객체의 price 값으로 큰 값을 리턴하는 BinaryOperator API 정의
        binaryOperator = BinaryOperator.maxBy((f1, f2) -> Integer.compare(f1.getPrice(), f2.getPrice()));
        fruit = binaryOperator.apply(딸기, 수박);
        System.out.println("maxby Operation result : " + fruit.getName());

    }
}