모던 자바 인 액션 3장 람다 표현식
람다 표현식은 파라미터, 화살표, 바디로 이루어진다.
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
람다 파라미터 / 화살표 / 람다 바디
- 89p
여러가지 람다 표현식
(String s) -> s.lenght() // String 형식의 파라미터 하나를 가지며 int를 반환한다.
(Apple a) -> a.getWeight() > 150 // Apple 형식의 파라미터 하나를 가지면 boolean을 반환한다.
(int x, int y) -> { System.out.prinln(x + y); } // int 형식의 파라미터 두 개를 가지며 리턴값이 없다.
() -> 45 // 파라미터가 없으며 int 42를 반환한다.
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())) // Apple 형식의 파라미터 두 개를 가지며 int를 반환한다.
- 90p
람다 예제
불리언 표현식 (List<String> List ) -> list.isEmpty()
객체생성 () -> new Apple(10)
객체에서 소비 (Apple a) -> { System.out.println(a.getWeight());
객체에서 선택/추출 (String s) -> s.length()
두 값을 조합 (int a, int b) -> a * b
두 객체 비교 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
- 91p
함수형 인터페이스 : 오직 하나의 추상 메서드를 지정하는 인터페이스
public interface Predicate<T> {
boolean test (T t);
}
- 92p
함수형 인터페이스로 뭘 할 수 있을까? 람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있으므로 전체 표현식을 함수형 인터페이스의 인스턴스로 취급(기술적으로 따지면 함수형 인터페이스를 구현한 클래스의 인스턴스)할 수 있다.
- 93p
Runnable r1 = () -> System.out.println("hello");
Runnable r2 = new Runnable() {
public void run() {
System.out.println("hello")
}
};
public static void process(Runnable r) {
r.run();
}
- 94p
동작을 적용하여 실행 어라운드 패턴을 구현한 예시
String result = processFile(BufferedReader br) -> br.readLine() + br.readLine());
- 98p
Predicate 인터페이스를 사용할 때 String 객체를 인수로 받는 람다 예시
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> results = new ArrayList<>();
for (T t: list) {
if (p.test(t)) {
results.add(t);
}
}
return results;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> is.Empty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
- 101p
자바에서는 기본형을 참조형으로 변환하는 기능을 제공한다. 이 기능을 박싱이라고 한다. 참조형을 기본형으로 변환하는 반대 동작을 언박싱이라고 한다.
- 103p
람다 표현식에서는 익명 함수가 하는 것처럼 자유 변수를 활용할 수 있다. 이와 같은 동작을 람다 캡쳐링이라고 부른다. 다음은 portNumber 변수를 캡처하는 람다 예제다.
int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
- 113p
메서드 참조
기존 코드
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight?()));
다음은 메서드 참조와 java.util.Comparator.comparing을 활용한 코드다.
inventory.sort(comparing(Apple::getWeight));
- 115p
명시적으로 메서드명을 참조함으로써 가독성을 높일 수 있다. 메서드 참조는 어떻게 활용할까? 메서드명 앞에 구분자(::)를 붙이는 방식으로 메서드 참조를 활용할 수 있다. 예를 들어 Apple::getWeight는 Apple 클래스에 정의된 getWeight의 메서드 참조다. 실제로 메서드를 호출하는 것은 아니므로 괄호는 필요없음을 기억하자. 결과적으로 메서드 참조는 람다 표현식 (Apple a) -> a.getWeight()를 축약한 것이다.
- 115p
1. 정적 메서드 참조
- 예를 들어 Integer의 parseInt 메서드는 Integer::parseInt로 표현할 수 있다.
2. 다양한 형식의 인스턴스 메서드 참저
- 예를 들어 String length 메서드는 String::length로 표현할 수 있다.
3. 기존 객체의 인스턴스 메서드 참조
- 예를 들어 Transaction 객체를 할당받은 expensiveTransaction 지역 변수가 있고 Transaction 객체에는 getValue 메서드가 있다면, 이를 expensiveTransaction::getValue라고 표현할 수 있다.
- 116p
(args) -> ClassName.staticMethod(args)
=> ClassName::staticMethod
(arg0, rest) -> arg0.instanceMethod(rest)
=> ClassName::instanceMethod
(args) -> expr.instanceMethod(args)
=> expr::instanceMethod
- 117p
생성자 참조
Supplier<Apple> c1 = Apple::new = ()->new Apple();
- 119p
Apple(Integer weight)라는 시그니처를 갖는 생성자는 Function 인터페이스의 시그니처와 같다.
=>
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);
=>
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);
map 메서드를 이용해 Apple 생성자로 전달하기.
List<Integer> weights = Arrays.asList(7, 3, 4, 10);
List<Apple> apples = map(weights, Apple::new);
public List<Apple> map(List<Integer> list, Function<Integer, Apple> f) {
List<Apple> result = new ArrayListM<>();
for (Ineger i:list){
result.add(f.apply(i));
}
return result;
}
- 119p
다음과 같은 코드를 만들기
inventory.sort(comparing(Apple::getWeight));
1단계 : 코드 전달
inventory.sort(new AppleComparator());
public class AppleComparator implements Comparator<Apple> {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
}
자바 api에 구현되어 있는 sort 메서드는 Comparator 객체를 인수로 받아 두 사과를 비교한다. 객체 안에 동작을 포함시키는 방식으로 다양한 전략을 전달할 수 있다.
=> 'sort의 동작은 파라미터화되었다.'라고 말할 수 있다.
2단계 : 익명 클래스 사용
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
=> 한 번만 사용할 Comparator를 위 코드처럼 구현하는 것 보다는 익명 클래스를 이용하는 것이 좋다.
3단계 : 람다 표현식 사용
추상 메서드의 시그니처(함수 디스크립터라 불림)는 람다 표현식의 시그니처를 정의한다. Comparator의 함수 디스크립터는 (T, T) -> int다. 우리는 사과를 사용할 것이므로 정확히는 (Apple, Apple) -> int로 표현할 수 있다.
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
inventory.sort(( a1, a2) -> a1.getWeight().compareTo(a2.getWeight())
Comaparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());
=>
inventory.sort(comparing(apple -> apple.getWEight());
4단계 : 메서드 참조 사용
inventory.sort(comparing(Apple::getWeight));
=> Apple을 weight별로 비교해서 inventory를 sort하라 !
- 121 ~ 122p
Comparator 조합
- reversed()
- thenComparing
Predicate 조합
Predicate<Apple> redAndHeavyApple = redApple.and(apple -> apple.getWeight() > 150 );
- 124 ~ 125p
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> f = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g); // 수학적으로 g(f(x))를 표현
andThen이 아니라 f.compose(g)로 하면 수학적으로f(g(x))로 표현
- 126p
'Book' 카테고리의 다른 글
[독서 기록] 모던 자바 인 액션 6장 스트림으로 데이터 수집 (0) | 2023.01.08 |
---|---|
[독서 기록] 모던 자바 인 액션 4장 스트림 소개, 5장 스트림 활용 (0) | 2023.01.08 |
[독서 기록] 모던 자바 인 액션 2장 - 동작 파라미터화 코드 전달하기 (0) | 2023.01.04 |
[독서 기록] 모던 자바 인 액션 1장 자바 8, 9, 10, 11 : 무슨 일이 일어나고 있는가? (0) | 2023.01.04 |
[독서 기록] 객체지향의 사실과 오해 (1) | 2022.12.08 |