-
[Java] 람다식 - 표준 API의 함수적 인터페이스2CSE/Java 2015. 9. 23. 14:03
람다식은 여러 절로 구성되어 있습니다.
andThen()과 compose() 디폴트 메소드
디폴트 및 정적 메소드는 추상 메소드가 아니기 때문에 함수적 인터페이스에 선언되어도 여전히 함수적 인터페이스의 성질을 잃지 않습니다. 여기서 함수적 인터페이스 성질이란 하나의 추상 메소드를 가지고 있고 람다식으로 익명 구현 객체를 생성할 수 있는 것을 말합니다. java.util.function 패키지의 함수적 인터페이스는 하나 이상의 디폴트 및 정적 메소드를 가지고 있습니다.
Comsumer, Function, Operator 종류의 함수적 인터페이스는 andThen()과 compose() 디폴트 메소드를 가지고 있습니다.
andThen() 과 compose() 메소드는 두 개의 함수적 인터페이스를 순차적으로 연결하고, 첫 번째 처리 결과를 두 번째 매개값으로 제공해서 최종 결과값을 얻을 때 사용합니다.
andThen()과 compose()의 차이점은 어떤 함수적 인터페이스부터 먼저 처리하느냐에 따라 다릅니다.
123인터페이스AB = 인터페이스A.andThen(인터페이스B);최종결과 = 인터페이스AB.method();cs 인터페이스 AB의 method()를 호출하면 우선 인터페이스 A부터 처리하고 결과를 인터페이스B의 매개값으로 제공합니다. 인터페이스 B는 제공받은 매개값을 가지고 처리한 후 최종 결과를 리턴합니다.
이번에는 compose()를 살펴보도록 하겠습니다. 인터페이스AB가 method()를 호출하면 우선 인터페이스 B부터 처리하고 결과를 인터페이스 A의 매개값으로 제공합니다. 인터페이스 A는 제공받은 매개값을 가지고 처리한 후 최종 결과를 리턴합니다.
12인터페이스AB = 인터페이스A.compose(인터페이스B);최종결과 = 인터페이스AB.method();cs 다음은 andThen()과 compose() 디폴트 메소드를 제공하는 java.util.function 패키지의 함수적 인터페이스입니다.
Consumer의 순차적 연결
Consumer 종류의 함수적 인터페이스는 처리 결과를 리턴하지 않기 때문에 andThen() 디폴트 메소드는 함수적 인터페이스의 호출 순서만 정합니다.
다음 예제는 Consumer<Member> 함수적 인터페이스 두 개를 순차적으로 연결해서 실행합니다. 첫 번째 Consumer<Member>는 이름을 출력하고, 두 번째 Consumer<Member>는 아이디를 출력합니다.
* Member.java
1234567891011121314151617181920212223242526272829303132333435363738394041package lambda;public class Member {private String name;private String id;private Address address;public Member(String name, String id, Address address) {super();this.name = name;this.id = id;this.address = address;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getId() {return id;}public void setId(String id) {this.id = id;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}}cs * Address.java
123456789101112131415161718192021222324252627282930package lambda;public class Address {private String country;private String city;public Address(String country, String city) {super();this.country = country;this.city = city;}public String getCountry() {return country;}public void setCountry(String country) {this.country = country;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}}cs * ConsumerAndThenExam.java
1234567891011121314151617181920package lambda;import java.util.function.Consumer;public class ConsumerAndThenExam {public static void main(String[] args) {Consumer<Member> consumerA = (m) -> {System.out.println("conusmerA: " + m.getName());};Consumer<Member> consumerB = (m) -> {System.out.println("consumerB: " + m.getId());};Consumer<Member> consumerAB = consumerA.andThen(consumerB);consumerAB.accept(new Member("Jolie", "jolie34", null));}}cs Function의 순차적 연결
Function과 Operator 종류의 함수적 인터페이스는 먼저 실행한 함수적 인터페이스의 결과를 다음 함수적 인터페이스의 매개값으로 넘겨주고, 최종 처리 결과를 리턴합니다.
예를 들어 Function<Member, Address>와 Function<Address, String>을 순차적으로 연결해서 Function<Member, String>을 생성한다고 가정해봅시다.
Function<Member, Address>는 매개값으로 제공되는 Member로부터 Address를 리턴합니다. Function<Address, String>은 매개값으로 제공되는 Address로부터 String을 리턴합니다. 이 둘을 andThen()이나 compose()로 연결하면 Function<Member, Address>에서 리턴한 Address를 Function<Address, String>의 매개값으로 넘겨서 최종 String 타입을 리턴하는 Function<Member, String>을 생성해 냅니다.
Address는 두 함수적 인터페이스 간의 전달 데이터입니다. Address는 내부적으로 전달되기 때문에 최종 함수적 인터페이스의 형태는 입력 데이터가 Member, 출력 데이터가 String이 되는 Function<Member, String>이 됩니다.
다음 예제는 Member 객체의 필드인 Address에서 city 정보를 얻기위해 두 Functional interface를 andThen()과 compose()를 이용해서 순차적으로 연결했습니다.
* FunctionAndThenComposeExam.java
12345678910111213141516171819202122232425262728package lambda;import java.util.function.Function;public class FunctionAndThenComposeExam {public static void main(String[] args) {Function<Member, Address> functionA;Function<Address, String> functionB;Function<Member, String> functionAB;String city;functionA = (m) -> m.getAddress();functionB = (a) -> a.getCity();functionAB = functionA.andThen(functionB);city = functionAB.apply(new Member("Jolie", "jolie34", new Address("France", "Cannes")));System.out.println("거주 도시: " + city);}}cs and(), or(), negate() 디폴트 메소드와 isEqual() 정적 메소드
Predicate 종류의 함수적 인터페이스는 and(), or(), negate() 디폴트 메소드를 가지고 있습니다.
이 메소드들은 각각 논리 연산자인 &&, ||, !과 대응된다고 볼 수 있습니다.
다음 에제는 2의 배수와 3의 배수를 조사하는 두 Predicate를 논리 연산한 새로운 Predicate를 생성합니다.
* PredicateAndOrNegateExam.java
1234567891011121314151617181920212223242526272829package lambda;import java.util.function.IntPredicate;public class PredicateAndOrNegateExam {public static void main(String[] args) {IntPredicate predicateA = a -> a % 2 == 0;IntPredicate predicateB = b -> b % 3 == 0;boolean result;IntPredicate 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);predicateAB = predicateA.negate();result = predicateAB.test(9);System.out.println("9는 홀수입니까? " + result);}}cs Predicate<T> 함수적 인터페이스는 and(), or(), negate() 디폴트 메소드 이외에 isEqual() 정적 메소드를 추가로 제공합니다.
isEqual() 메소드는 test() 매개값인 sourceObject와 isEqual() 의 매개값인 targetObject를 java.util.Objects 클래스의 eqauls()의 매개값으로 제공하고, Objects, eqauls(source, targetObject)의 리턴값을 얻어 새로운 Predicate<T>를 생성합니다.
123Predicate<Object> predicate = Predicate.isEqual(targetObject);boolean result = predicate.test(sourceObject);cs Object.eqauls(sourceObject, targetObject)는 다음과 같은 리턴값을 제공합니다.
다음 예제는 두 문자열을 비교하기 위해 Predicate의 isEqual() 정적 메소드를 사용했습니다.
* PredicateIsEqualExam.java
1234567891011121314151617181920212223242526package lambda;import java.util.function.Predicate;public class PredicateIsEqualExam {public static void main(String[] args) {Predicate<String> predicate;predicate = Predicate.isEqual(null);System.out.println("null, null: " + predicate.test(null));predicate = Predicate.isEqual("Java");System.out.println("Java, null: " + predicate.test(null));predicate = Predicate.isEqual(null);System.out.println("null, Java: " + predicate.test("Java"));predicate = Predicate.isEqual("Java");System.out.println("Java, Java: " + predicate.test("Java"));}}cs minBy(), maxBy() 정적 메소드
BinaryOperator<T> 함수적 인터페이스는 minBy()와 maxBy() 정적 메소드를 제공합니다.
이 두 메소드는 매개값으로 제공되는 Comparator를 이용해서 최대 T와 최소 T를 얻는 BinaryOperator<T>를 리턴합니다.
Comparator<T>는 다음과 같이 선언된 함수적 인터페이스입니다.
o1과 o2를 비교해서 o1이 작으면 음수를, o1과 o2가 동일하면 0을, o1이 크면 양수를 리턴하는 compare() 메소드가 선언되어 있습니다.
12345@FunctionalInterfacepublic interface Comparator<T> {public int compare(T o1, T o2);}cs Comparator<T>를 타겟 타입으로 하는 람다식은 다음과 같이 작성할 수 있습니다.
12(o1, o2) -> { ...; return int_value; }cs 만약 o1과 o2가 int 타입이라면 다음과 같이 Integer.compare(int, int) 메소드를 이용할 수 있습니다. Integer.compare()는 첫 번째 매개값이 두 번째 매개값보다 작으면 음수, 같으면 0, 크면 양수를 리턴합니다.
1(o1, o2) -> Integer.compare(o1, o2);cs 다음 예제는 두 과일의 값을 비교해서 값이 낮거나 높은 과일을 얻어냅니다.
* Fruit.java
1234567891011121314package set;public class Fruit {public String name;public int price;public Fruit(String name, int price) {super();this.name = name;this.price = price;}}cs * OperatorMinByMaxByExam.java
12345678910111213141516171819202122232425package lambda;import java.util.function.BinaryOperator;import set.Fruit;public class OperatorMinByMaxByExam {public static void main(String[] args) {BinaryOperator<Fruit> binaryOperator;Fruit fruit;binaryOperator = BinaryOperator.minBy((f1, f2) -> Integer.compare(f1.price, f2.price));fruit = binaryOperator.apply(new Fruit("Strawberry", 5000), new Fruit("Graph", 9000));System.out.println(fruit.name);binaryOperator = BinaryOperator.maxBy((f1, f2) -> Integer.compare(f1.price, f2.price));fruit = binaryOperator.apply(new Fruit("Strawberry", 5000), new Fruit("Graph", 9000));System.out.println(fruit.name);}}cs * 이 포스트은 서적 '이것이 자바다' 를 참고하여 작성한 포스트입니다.
'CSE > Java' 카테고리의 다른 글
[Java] 멀티 스레드 - 작업 스레드 (0) 2015.12.12 [Java] 멀티 스레드 (0) 2015.12.12 [Java] 람다식 - 메소드 참조 (0) 2015.09.23 [Java] 람다식 - 표준 API의 함수적 인터페이스1 (0) 2015.09.20 [Java] 람다식 - 클래스 멤버와 로컬 변수 사용 (0) 2015.09.20 [Java] 람다식 - 기본 문법, 타겟 타입과 함수적 인터페이스 (0) 2015.09.20