함수형 인터페이스의 사용
줄곧 말하지만 함수형 인터페이스는 오직 하나의 추상 메서드 만을 갖는다.
함수형 인터페이스 내부의 추상 메서드는 : 함수 디스크립터라고 불린다.
이전에는 우리가 함수형 인터페이스를 직접 만들어 사용하였지만 자바에서 미리 만들어놓은 함수형 인터페이스들이 존재한다.
1. Predicate
활용
list와 Predicate를 파라미터로 받는 filter 메서드 생성
public static <V> List<V> filter(List<V> list, Predicate<V> p) {
List<V> result = new ArrayList<>();
for(V s : list) {
if(p.test(s)) {
result.add(s);
}
}
return result;
}
파라미터로 던질 예시 list
List<String> arrayStrings = Arrays.asList("choi", "", "jun", "", "woo");
실행
List<String> nonEmpty = filter(arrayStrings, ((String s)->!s.isEmpty()));
리펙토링
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(arrayStrings, nonEmptyStringPredicate);
2. Consumer
제네릭 형식의 객체를 받아와서 void를 반환하는 추상 메서드를 정의 (accept)
list의 size 만큼 전략을 실행하는 메서드 생성
public static <T> void forEach(List<T> list, Consumer<T> c) {
for(T t : list)
c.accept(t);
}
Consumer로 리스트의 내용을 출력하는 전략을 전송
List<Integer> arrayIntegers = Arrays.asList(1, 2, 3, 4, 5);
forEach(arrayIntegers, (Integer i) -> System.out.println(i));
3. Function
T로 받아서 R로 리턴
List를 받아서 결과 리스트에 저장하는 메서드 작성
public static <T,R> List<R> map(List<T> list, Function<T,R> f) {
List<R> result = new ArrayList<>();
for(T t : list) {
result.add(f.apply(t));
}
return result;
}
Function에 리스트의 길이를 반환하는 전략 전송
List<String> stringList = Arrays.asList("choi", "jun", "woo");
List<Integer> map = map(stringList, (String s) -> s.length());
기본형에 특화
: 예를 들어 제네릭을 받을 땐 참조형(Object, List, Integer, Byte) 밖에 구조상 받을 수 없다.
- 따라서 오토 박싱을 하게 되는데 이는 너무 많은 비용이 든다.
오토 박싱을 하지 않고 캐스팅을 하는 함수형 인터페이스가 자바 8에는 존재한다.
람다, 함수형 인터페이스의 예외 사용 밥법 두 가지
1. 인터페이스에 예외를 직접 정의
2. 람다에 try catch로 예외를 정의
형식검사, 형식 추론, 제약
람다는 어떤 함수형 인터페이스의 메서드를 구현하는지 알 방법이 없다.
1. 형식검사 : 람다가 사용하는 context를 이용해서 람다의 형식을 알 수 있다.
2. 같은 람다 다른 함수형 인터페이스
: 같은 람다라도 다른 함수형 인터페이스의 추상 메서드를 사용할 수 있다.
- 특별한 void 호환 규칙 : 람다의 바디에 일반 표현식이 존재하면 함수형 인터페이스의 리턴 값이 달라도 가능!(return void 만 가능)
3. 형식 추론
단순히 말하자면 람다식에 타입을 제외하고 변수만 선언할 수 있다는 말.
- 지역변수 사용 가능 :
전달받은 파라미터뿐만 아니라 지역변수도 사용 가능 하지만 지역변수는 final(상수)의 역할을 가진 변수만이 적용 가능하다.
- 이유는 인스턴스 변수는 힙에 저장되어 객체가 소멸하는 순간까지 이어지지만 지역변수는 스택에 저장되어 변수의 사용이 달라지거나 끝나면 해당 변수를 람다가 참조할 수없기 때문이다.
메서드 레퍼런스
메서드 레퍼런스를 사용하는 이유는?
만약 람다가 메서드 호출을 명령한다면 어떤 메서드를 호출할 것인지에 대한 설명보단!
메서드명을 직접 참조하는 것이 가독성에 좋기 때문이다.
활용법 : " :: "
(Apple a) -> a.getWeight() Apple :: getWeight
메서드 레퍼런스를 만드는 방법 세 가지
- 정적 메서드 레퍼런스
Ex) Integer의 parseInt 메서드의 표현 : Integer :: parseInt
- 다양한 형식의 인스턴스 메서드 레퍼런스
Ex) String :: length
- 기존 객체의 인스턴스 메서드 레퍼런스
- 외부 객체의 메서드를 호출 시
( ) -> expensiveTransaction.getValue() //expensiveTransaction : Transaction 객체를 할당받은 참조 변수 expensiveTransaction :: getValue
특별한 형식의 메서드 레퍼런스
ex) 대소문자 구분 없이 리스트를 정렬하는 코드
람다 표현식을 레퍼런스로 바꾸는 세 가지 방식
Code Link
https://github.com/mike6321/PURE_JAVA/tree/master/Java8/2Week/lamda
References
|
'Java > Java8' 카테고리의 다른 글
(JAVA8) 함수형 인터페이스와 람다 표현식 (1) (0) | 2020.07.07 |
---|---|
(JAVA8) 스트림의 활용 (2) (0) | 2020.01.19 |
(JAVA8) 람다식으로 향하는 과정 (0) | 2020.01.16 |
(JAVA8) 스트림의 개요 (1) (0) | 2020.01.03 |
(JAVA8) 람다식 (1) (0) | 2020.01.03 |