현재 보고 있는 책인 JAVA8 IN ACTION 에서는 람다에 대해서 너무 자세히 다루고 있습니다. 

덕분에 많은 공부가 되었고, 포스팅 개수도 많이 늘어나고 있는 것 같습니다. ㅡㅡ^


오늘은 람다에 대한 마지막 포스팅은 람다를 연결해서 사용하는 방법에 대해 알아보려고 합니다. 


람다가 수학식에 대하여 추상화를 한 것이라 했을 때, 우리는 각 식을 조합하여 복잡한 식을 만들 수 있다는 것을 생각해 볼 수 있습니다. 

(잘 생각해보면, 굳이 람다가 아니더라도 if 절을 사용할 때 and 나 or 등을 사용하여 복잡한 컨디션을 만들고 있지 않나요?)


JAVA8은 람다 표현식을 더욱 적극적으로 사용할 수 있도록, 람다를 조합을 하거나 혹은 편리하게 사용하게 해줄 수 있는 메소드를 제공해줍니다.


1. Comparator 


- reverse 메소드를 이용하여 역정렬을 사용할 수 있습니다.


1
2
3
4
5
List<Apple> dataList = new ArrayList<>();
Comparator<Apple> compare = Comparator.comparing(Apple::getWeight);
dataList.sort(compare);
dataList.sort(compare.reversed());
 
cs


- thenComparing 메소드를 조건을 더 연결할 수 있습니다.


1
2
// 무게가 같다면, 컬러로 
dataList.sort(Comparator.comparing(Apple::getWeight).reversed().thenComparing(Apple::getColor));
cs



2. Predicate


- negate 메소드를 사용하여 반대 (!) 를 사용할 수 있습니다.


1
2
3
4
5
6
BiFunction<String, Integer, Apple> creator2 = Apple::new;
Apple apple2 = creator2.apply("green"100);
 
Predicate<Apple> isNotHeavy = (v) -> v.getWeight() > 150;
System.out.println(isNotHeavy.negate().test(apple2)); // true 
 
cs


- and나 or를 사용할 수 있습니다.


1
2
3
4
5
Predicate<Apple> isNotHeavy = (v) -> v.getWeight() > 150;
 
// 사과의 무게는 150 이상 200 이하 이거나, 색이 green 인 경우
isNotHeavy.and((v) -> v.getWeight() < 200).or((v) -> v.getColor().equals("green")).test(apple2);
 
cs



3. Function


우리가 보통 함수를 처음 배울 때, 이러한 표현을 배운적이 있습니다.


f(x) = x + 1

g(x) = x * 2


g(f(x)) = (x + 1) * 2


위와 같은 역할을 해줄 수 있는 메소드가 존재합니다. (andThen, compose)


1
2
3
4
5
6
Function<Integer, Integer> f = (x) -> x + 1;        // f(x)
Function<Integer, Integer> g = (x) -> x * 2;        // g(x)
        
Function<Integer, Integer> fg = f.andThen(g);     // g(f(x))
Function<Integer, Integer> gf = f.compose(g);    // f(g(x))
 
cs



람다를 이어줌으로써, 복잡한 식을 보다 쉽게 작성할 수 있지만 아무래도 복잡한 식에 대해서는 메서드 레퍼런스를 사용하는 것이 좋아보입니다.


만드는 것은 간단해보일 수 있으나, 유지보수적인 면도 봐야겠죠. ㅎㅎ



이 것으로 람다에 대한 포스팅은 끝!!!!  :-)


다음 포스팅부터는 자바8의 꽃인 Stream API에 대해 다루어 보려 합니다.



자바 8 인 액션
국내도서
저자 : 라울-게이브리얼 우르마(RAOUL-GABRIEL URMA),마리오 푸스코(MARIO FUSCO),앨런 마이크로프트(ALAN MYCROFT) / 우정은역
출판 : 한빛미디어 2015.04.01
상세보기


반응형

'개발이야기 > 함수형 프로그래밍' 카테고리의 다른 글

Stream과 Collection 차이  (0) 2016.08.04
Stream API  (0) 2016.08.03
메서드 레퍼런스  (0) 2016.08.01
람다의 실제 형식  (0) 2016.07.28
클로저와 람다  (2) 2016.07.28
Posted by N'

계속해서 람다와 관련된 포스팅을 하고 있습니다. 


람다는 익명 클래스를 어느정도 대체를 해줄 수 있는 것처럼 보이며, 간단한 처리는 대부분 람다를 이용하게 되지 않을까요? 

(예를들어 안드로이드에서 이벤트 처리 시, 익명클래스의 대체가.. ㅡㅡ^ ) 


람다와 람다를 왜 써야하는지 보려면 이 곳으로...



오늘은 람다표현식과 더불어, 간결성과 더불어, 가독성까지 챙길 수 있는 메서드 레퍼런스에 대하여 포스팅하려 합니다.


JAVA 8 IN ACTION 에 따르면, 메서드 레퍼런스는 특정 메서드만을 호출하는 람다의 축양형 이라고 볼 수 있다고 합니다. 

즉 람다를 통해 어떻게 어떤식의 메서드를 사용하라가 아닌, 메서드의 이름만을 참조하여 처리한다고 볼 수 있습니다. (그래서 레퍼런스라고 하나 봅니다... ㅎㅎ)


예를 들어, 이런 것을 볼 수 있을 것 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
class Apple {
    private int weight;
 
    public int compareTo(Apple apple){
        if (weight >= apple.getWeight()) return 0;
        else if (weight <= apple.getWeight()) return 1;
        else return -1;
    }
}
 
inventory.sort((Apple a, Apple b) -> a.compareTo(b));
inventory.sort(Apple::compareTo); // -> 메서드 레퍼런스
 
cs


저는 이 것을 보면서 처음에 이해가 안됐던 부분이 파라미터는 어떻게 넣을 것이냐라는 것이었습니다. 

람다의 경우 파라미터 영역이 따로 명시되어 있지만 메서드 레퍼런스는 (Apple::compareTo) 다음과 같이 명시가 되어있지 않습니다. 

그러나 이 것은 sort 매개변수 넣을 당시, 람다표현식에 대한 형식검사를 함을 알 수 있습니다. sort의 파라미터로 람다 표현식을 사용 시, 람다 디스크립터를 맞춰야 한다는 것은 지난 포스팅 때 언급을 했었습니다.




즉 이런한 점을 이용해, 람다 디스크립터에 맞춰 메서드 레퍼런스를 사용해주면 됨을 알게 되었습니다.


JAVA8 IN ACTION 에서는 메서드 레퍼런스를 만드는 법은 크게 세가지로 보고 있습니다.


1. 정적 메서드 레퍼런스 (정적 메서드 - static 메서드라 생각합시다.)


예를들어 Integer 클래스의 parseInt 메서드는 정적 메서드로, String 객체를 Integer 형태로 변경해줍니다. 해당 메서드는 다음과 같이 인수로써 가질 수 있습니다.


1
2
3
Function<String, Integer> parse = Integer::parseInt;
int a = parse.apply("5");
 
cs



2. 다양한 형식의 인스턴스 메서드 레퍼런스 (일반 메서드라 생각합시다.)


클래스에 정의 된 일반 메서드를 인수로 가질 수 있음을 말합니다. 아래는 List의 메소드인 size() 를 인수로 가지는 예제입니다.


1
2
3
4
5
6
7
8
9
10
List<String> strList = new ArrayList<String>() {
    {
        add("A");
        add("B");
        add("C");
    }
};
 
Function<List, Integer> size = List::size;    // 일반 메서드의 메서드 레퍼런스
System.out.println(size.apply(strList));     // 3 
cs




3. 기존 객체의 인스턴스 메서드 레퍼런스 


인스턴스 객체를 이용하여, 메서드 레퍼런스를 사용할 수 있습니다. 

예를들면 이런식으로 사용이 가능해보입니다. 


1
2
3
4
5
6
7
8
9
10
List<String> strList = new ArrayList<String>() {
    {
        add("A");
        add("B");
        add("C");
    }
};
        
Consumer<String> consume = strList::add
consume.accept("E");
cs


메소드레퍼런스를 사용하려는 대상 메소드가 다형성에 의해 가려질 때 사용할 듯 합니다.



4. 생성자 레퍼런스


new 키워드를 사용하여, 생성자의 레퍼런스를 만들 수 있습니다. 아래와 같이 작성이 가능합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Apple {
    private String color;
    private int weight;
    
    public Apple(){}
    
    public Apple(String color, Integer weight){
        this.color = color;
        this.weight = weight;
    }
}
 
Supplier<Apple> creator = Apple::new;    // 기본 생성자에 대한 메서드 레퍼런스
Apple apple = creator.get(); 
 
// color 와 weight 를 받아 Apple을 만드는 생성자의 메서드 레퍼런스
BiFunction<String, Integer, Apple> creator2 = Apple::new
Apple apple2 = creator2.apply("green"100);
 
cs


위와 같이 다양한 시그니처 생성자에 대하여 메서드 레퍼런스를 사용할 수 있습니다.



이러한 메서드 레퍼런스를 람다표현식을 직접 사용하지 않고 사용하는 이유는 복잡한 람다식을 일일이 명시하는 것보다 가독성이 좋기 때문입니다. 


즉 람다식이 복잡하다면, 차라리 메소드로 정의하여 처리하는 것이 좋아보입니다.



자바 8 인 액션
국내도서
저자 : 라울-게이브리얼 우르마(RAOUL-GABRIEL URMA),마리오 푸스코(MARIO FUSCO),앨런 마이크로프트(ALAN MYCROFT) / 우정은역
출판 : 한빛미디어 2015.04.01
상세보기




반응형

'개발이야기 > 함수형 프로그래밍' 카테고리의 다른 글

Stream API  (0) 2016.08.03
람다 조합  (0) 2016.08.02
람다의 실제 형식  (0) 2016.07.28
클로저와 람다  (2) 2016.07.28
원시타입을 위한 함수형 인터페이스  (0) 2016.07.28
Posted by N'

계속해서 람다에 대한 포스팅을 하고 있습니다. 


람다 표현식의 사용을 통해 우리는 함수형 인터페이스의 인스턴스를 만들 수 있다는 것을 알게 되었습니다.



그러나 사실 람다표현식만 보게 되면 어느 함수형 인터페이스를 구현하는지에 대한 정보는 없습니다. 

단지 함수형 인터페이스에서 구현할 함수 디스크립터와의 형식 검사를 통해 유효성을 가질 수 있으며, 또한 어떻게 보면 추론까지 가능합니다. 


오늘은 이러한 내용에 대해 포스팅을 하려 합니다. ㅡㅡ^


1. 형식 검사


일단 아래 코드를 먼저 보도록 하겠습니다.


1
2
3
4
5
6
7
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}
 
Predicate<Apple> predicate = (Apple a) -> "green".equals(a.getColor());
 
cs


람다 표현식 (Apple a) -> "green".equals(a.getColor()); 이 Predicate<Apple>에 대입은 test 라는 한 개의 추상 메소드에 대한 함수 디스크립터에서 묘사해야할 내용과 같음을 알 수 있습니다. 


즉 추상 메소드의 시그니처가 요구하는 요구사항을 맞춰 람다를 제작해주어야 합니다.


이 때, void 의 경우 특별한 호환 규칙이 존재합니다.


1
2
3
4
5
6
public boolean checkData(Integer a){
    return a != null;
}
 
Consumer<Integer> setData = (Integer a) -> checkData(a);
setData.accept(5);
cs


위의 예제의 경우 람다 표현식 (Integer a) -> checkData(a); 의 경우 Integer를 파라미터로 받아 boolean 을 출력함을 알 수 있습니다. 


하지만 이 것은 유효한 코드입니다. 

(프로시저에서 boolean 함수를 사용한 것과 비슷하다고 생각하면 자연스럽게 넘어갈 수 있는 문제인 것 같습니다. ㅡㅡ^)


2. 형식 추론


함수형 인터페이스가 선언된 대상 형식(ex. Consumer<Apple>)에 따라, 람다 표현식의 형식 추론을 할 수 있습니다. 

대상 형식을 통해 함수 디스크립터(ex. (Integer a) -> a +2)를 알 수 있으며, 다음과 같은 코드가 가능합니다.


1
Consumer<Integer> setData = (a) -> checkData(a) ;
cs


대상형식 Consumer<Integer> 에 의해 람다 파라미터 부분에 Integer 형태가 이미 들어갈 것이라는 것을 추론할 수 있으며, 그렇기 때문에 단순히 a를 쓰는 것으로 대체가 가능합니다.


우리는 이미 이런 것을 제네릭을 사용하면서 많이 봐왔습니다. 

아래와 같이 말이죠.


1
List<Integer> dataList = new ArrayList<>();
cs



자바 8 인 액션
국내도서
저자 : 라울-게이브리얼 우르마(RAOUL-GABRIEL URMA),마리오 푸스코(MARIO FUSCO),앨런 마이크로프트(ALAN MYCROFT) / 우정은역
출판 : 한빛미디어 2015.04.01
상세보기



반응형

'개발이야기 > 함수형 프로그래밍' 카테고리의 다른 글

람다 조합  (0) 2016.08.02
메서드 레퍼런스  (0) 2016.08.01
클로저와 람다  (2) 2016.07.28
원시타입을 위한 함수형 인터페이스  (0) 2016.07.28
JAVA8 에 정의된 함수형 인터페이스  (0) 2016.07.28
Posted by N'