publicstaticvoidrunTest4Function(){ //String을 전달받아서 해당 문자열의 길이를 반환 Function_Exam<String, Integer> testFunc = (String s) -> s.length(); for(Integer i : checkData(makeStringList(), testFunc)) { System.out.println("i = " + i); } } /** * Function을 테스트 하는 메서드 * @param list String을 담은 리스트 * @param f 함수형 인터페이스 (Function) * @return List<R> 을 반환 */ publicstatic <T, R> List<R> checkData(List<T> list, Function_Exam<T, R> f){ List<R> resultList = new ArrayList<>(); for(T t : list) { resultList.add(f.apply(t)); } return resultList; }
아래는 Function의 다른 예제코드이다.
1 2 3 4 5 6 7 8 9 10 11
// andThen - 주어진 함수를 먼저 적용한 결과를 다른 함수의 입력으로 전달하는 함수를 반환 Function<Integer, Integer> f = x -> x+1; Function<Integer, Integer> g = x -> x*2; Function<Integer, Integer> h = f.andThen(g);; int result = h.apply(1); // 4를 반환
//compose - 인자로 주어진 함수를 먼저 실행한 다음에 그 결과를 외부 함수의 인수로 제공 Function<Integer, Integer> f = x -> x+1; Function<Integer, Integer> g = x -> x*2; Function<Integer, Integer> h = f.compose(g);; int result = h.apply(1); // 3를 반환
기본형 특화
위에서 간단하게 살펴본 제너릭 함수형 인터페이스는 모두 참조형만을 사용하였다.
사유는 제네릭 파라메터에서는 참조형만 사용이 가능하기 때문이다.
기본형에서 참조형으로 형 변환을 박싱(Boxing) 이라 한다.
참조형에서 기본형으로 형 변환을 언박싱(Unboxing) 이라 한다.
박싱, 언박싱을 자동으로 이뤄지게 해주는 것을 오토박싱(Autoboxing) 이라 한다.
박싱의 경우 기본형을 감싸는 레퍼형 클래스로써 힙에 저장이 되고, 이로 인해 박싱한 값은 메모리를 더 소비하고, 기본형으로 가져올 때도 메모리 탐색 등의 과정으로 인해 소모가 발생한다.
자바8 에서는 기본형을 입출력으로 사용해야 하는 상황에서 오토박싱 동작을 피할 수 있도록 기본형 특화 함수형 인터페이스를 제공한다.
아래 표는 자바8의 대표적인 함수형 인터페이스를 나타낸다.
함수형 인터페이스
함수 디스크립터
기본형 특화
Predicate < T>
T -> boolean
IntPredicate, LongPredicate, DoublePredicate
Consumer< T>
T -> void
IntConsumer, LongConsumer, DoubleConsumer
Function< T,R>
T -> R
IntFunction< R >, IntToDoubleFunction, IntToLongFunction, LongFunction< R >, LongToDoubleFunction, LongToIntFunction, DoubleFunction< R>, ToIntFunction< T>, ToDoubleFunction< T>, ToLongFunction< T>
객체간 우선순위를 비교할때 사용하는 인터페이스인데 전통적으로 1회성 구현을 많이 하는 인터페이스이다.
람다의 등장으로 Comparator의 구현이 매우 간결해져 Comparable 인터페이스의 실효성이 많이 떨어진듯 하다.
1 2
Comparator<String> c = (str1, str2) -> str1.compareTo(str2); int result = c.compare("aaa", "bbb");
형식 검사
먼저 아래의 코드를 참고하자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
List<Apple> tempList = filterApples(appleList, (Apple a) -> a.getWeight()>70); ``` * 대략 람다식을 확인해보면 Apple 객체를 받아서 a의 무게가 70 이상일 경우를 찾는 것까지 추론할 수 있다. * 즉 람다 표현식 자체에서는 람다가 어떤 함수형 인터페이스를 구현했는지 정보는 알 수 없다. * 하지만 람다에 사용되는 컨텍스트(메서드 파라미터[전달인자], 할당 변수 등) 를 통해 람다의 형식을 추론할 수 있다. * 위의 컨텍스트를 통해서 기대되는 람다 표현식의 형식을 <b> 대상 형식 (Target Type)</b> 이라 한다.
/** * 실제 실행 코드 */ publicvoidrunCode(){ System.out.println("Sort before"); inventory.forEach(apple -> System.out.println("Apple weight = " + apple.getWeight() + " color = " + apple.getColor()));
//코드 전달 방법 //inventory.sort(new AppleComparator());
//익명 클래스 사용 // inventory.sort(new Comparator<Apple>() { // @Override // public int compare(Apple o1, Apple o2) { // return o1.getWeight().compareTo(o2.getWeight()); // } // });
//lambda 사용 //inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
//메서드 레퍼런스 사용 inventory.sort(Comparator.comparing(Apple::getWeight));
/** * 예제의 리스트를 사용하기 위한 메서드 * @param bf * @return */ private List<Apple> getAppleList(BiFunction<Integer, String, Apple> bf){ List<Apple> result = new ArrayList<>(); result.add(bf.apply(300, "Red")); result.add(bf.apply(110, "Green")); result.add(bf.apply(90, "RedBlue")); result.add(bf.apply(210, "RedGreen")); result.add(bf.apply(220, "GreenYellow")); return result; }
/** * Apple 객체의 무게를 비교하기 위한 내부 클래스 (코드 전달 예제) */ classAppleComparatorimplementsComparator<Apple> {
/** * 동일한 경우 0, 첫 값이 큰 경우 양수, 첫 값이 작은 경우 음수 * @param o1 * @param o2 * @return */ @Override publicintcompare(Apple o1, Apple o2){ return o1.getWeight().compareTo(o2.getWeight()); } } }
람다 표현식을 조합할 수 있는 유용한 메서드
Comparator, Predicate, Function 와 같은 함수형 인터페이스를 통해 람다식을 조합할 수 있도록 유틸리티 메서드를 제공한다.