자바스크립트를 사용하다보면 종종 클로저를 사용할 때가 있습니다. 아래와 같이 말이죠.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Vo() {
    var data;
    
    return {
        getData : function() {
            return data;
        },
 
        setData : function(value) {
            data = value;
        }
    }
}
 
var closure = Vo();
closure.setData('Hello world');
console.log(closure.getData()); // Hello world
cs


클로저의 구조를 보게되면 함수 안에 내부함수가 존재하며, 이 내부함수는 인스턴스같이 외부로 넘겨지기도 하고 비지역변수를 자유롭게 변경이 가능합니다. 


람다나 익명클래스 역시도 저 동작과 비슷한 작업을 수행할 수 있습니다. 

함수를 인수로 넘기는 것이 가능하며, 메소드 내부에 함수를 선언할 수 있습니다. 

(아래 예제는 메소드 내부에 람다를 표현했고, 외부변수 x를 변경하였습니다.)


1
2
3
4
5
6
7
8
9
private int x = 0;
    
public void func(){
    int data = 0;
        
    IntConsumer setData = (int a) -> x = a;
    setData.accept(5);
}
 
cs


"JAVA 8 IN ACTION" 에서는 이러한 점 때문에 람다가 클로저의 정의에 부합하는가에 대해서 다루고 있습니다. 


결론부터 말하면, 서로 비슷한 일을 하고는 있지만 람다의 경우 선언된 지역변수의 데이터를 변경할 수 없습니다. 

지역변수가 람다 내부에서 사용되려면 final 한 상태이어야 합니다. 혹은 final과 같은 동작을 하거나 말이죠 ㅡㅡ^

(아래 예제는 람다 표현식에서 지역변수 data를 변경하려 하지만, 이 것은 문법 오류라고 체크하게 됩니다.)


1
2
3
4
5
6
7
public void func(){
    int data = 0;
         // 문법 오류
    IntConsumer setData = (int a) -> data = a;
    setData.accept(5);
}
 
cs


위와 같은 이유는 지역 변수 값의 경우 스택에 존재하며, 해당 메소드의 스레드 생명주기와 동일하게 종료되어야 합니다. 

이것이 보장되지 않는다면 람다 내부 블럭이 실행 시, 안전하지 않은 동작이 수행되겠죠.

(멤버 변수의 경우 힙에 있으므로, 람다에 대해 특별한 제약이 없습니다.) 



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


반응형
Posted by N'

JAVA8에서는 람다 표현식을 조금 더 편리하게 사용할 수 있도록 미리 제네릭 형태의 interface를 제작했습니다.




그러나, 기존 제네릭을 쓰는 클래스 (이를테면 Collection 그룹) 들과 마찬가지로, 

원시타입의 함수형 인터페이스는 사용할 수 없는 것처럼 보였습니다. ㅜㅜ 


오늘 포스팅은 이런 점들을 위한 원시타입을 지원하는 함수형 인터페이스에 대해 알아보려 합니다.


일단 원시타입을 지원하지 않는다고 하더라도, 이미 자바에는 원시타입을 지원하기 위한 wrapper 클래스들이 존재합니다. 


(Integer, Boolean 등) wrapper 라는 이름에서 보듯이, 이 클래스를 이용하면,


원시타입을 wrapper 로 변경하는 박싱 (boxing) 작업

wrapper 클래스를 원시타입으로 변경하는 언박싱 (unboxing) 작업


을 수행할 수 있습니다. 


보통 파라미터로 데이터를 넘기거나, 타입변환 시 자동으로 해주는 작업(오토 박싱)이지만, 이러한 변환 과정은 결국 비용이 발생하는 작업입니다. 

(인스턴스를 생성한 작업이기 때문에 결국 힙 메모리에 저장하게 되고, 원시타입을 찾을 때 역시도 메모리를 탐색해야 합니다)


자바8에서는 많이 사용되는 원시타입이 오토박싱되는 작업을 피할 수 있도록 원시타입에 특화된 함수형 interface를 제공합니다.


예를들어, int형의 Predicate 를 사용한다고하면, 다음과 같이 사용할 수 있습니다.


1
IntPredicate predicate = (int a) -> a >= 50;
cs


위와 같이 원시타입의 함수형 인터페이스를 사용하고 싶다면, 앞에 원시타입의 이름을 붙인 인터페이스를 사용하면 됩니다. 

(double 형의 Consumer 면 DoubleConsumer 겠죠. ㅡㅡ^)



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


반응형

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

람다의 실제 형식  (0) 2016.07.28
클로저와 람다  (2) 2016.07.28
JAVA8 에 정의된 함수형 인터페이스  (0) 2016.07.28
함수형 인터페이스  (0) 2016.07.27
람다 표현식  (0) 2016.07.27
Posted by N'

우리는 람다가 왜 쓰이는 지에 대해서 알게 되었고, 람다를 사용하기 위해서는 함수형 인터페이스를 제작해야한다는 것을 알게 되었습니다. 




하지만 대부분 함수형에서 사용할 법한 함수 디스크립터는 비슷할 것이며, 그렇기 때문에 JAVA8 라이브러리 설계자들은 java.util.function에 미리 사용할 법한 함수 인터페이스를 정의했습니다. 

(Observer 패턴을 많이 사용한다고 Observerable 을 정의한 것과 같은 맥락이겠죠. ㅡㅡ^)


오늘 포스팅은 자주 쓰일 법한 JAVA8의 함수형 인터페이스를 알아보려 합니다. (인터페이스에 대한 표기는 함수 디스크럽터만 정의합니다. 디폴트 메소드는 생략하겠습니다. ㅜㅜ)


1. Predicate

boolean 형식이 필요한 상황에서 별다른 구현 없이 람다식을 사용할 수 있습니다.


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


2. Consumer

interface의 이름대로, 소비자입니다. 즉 프로시저로써, void 형의 함수를 만든다고 생각하면 됩니다. 


1
2
3
4
5
6
7
8
9
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
 
People doohyun = obj.createObject(27"남두현");
 
Consumer<People> cosumer = (People people) -> System.out.println(people.toString());
cosumer.accept(doohyun);
cs


3. Function

Function 인터페이스의 경우 제네릭 형식의 T를 받아 R 객체를 반환하는 apply 라는 추상 메소드를 가지고 있습니다.


1
2
3
4
5
6
7
@FunctionalInterface
public interface Function<T, R> {
    R accept(T t);
}
 
Function<String, Integer> function = (String name) -> name.length();
Integer blogNameLength = function.apply("남두현 블로그 입니다.");
cs


4. Supplier

Consumer와 반대로 제네릭 형식의 T를 출력해주는 인터페이스입니다.  


1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
public interface Supplier<T> {
    T accept();
}
 
Supplier<People> supplier = () -> {
    People ret = new People();
    ret.setName("기본 이름 지정");
    return ret;
};
cs


5. UnaryOperator

제네릭 형식의 T 타입을 파라미터로 받아 T를 return 하는 인터페이스다. 생각보다 쓸모는 없어보이네요다. 굳이 쓰자면 이런데에 쓸만하지 않을까요? (Clone 만들기)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@FunctionalInterface
public interface UnaryOperator<T> {
    T createObject(T t);
}
 
UnaryOperator<People> unaryOperator = (People people) -> {
    People clone = new People();
    clone.setAge(people.getAge());
    clone.setName(people.getName());
 
    return clone;
};
 
People doohyun = obj.createObject(27"남두현");
People cloneDoohyun = unaryOperator.apply(doohyun);
cs


6. BinaryOperator

제네릭 형식의 T 타입 두개를 받아, 조합하는 용도로 사용할 수 있다. 


1
2
3
4
5
6
7
@FunctionalInterface
public interface BinaryOperator<T> {
    T createObject(T t1, T t2);
}
 
BinaryOperator<Integer> binaryOperator = (Integer a, Integer b) -> a + b;
System.out.println(binaryOperator.apply(54));
cs


여기서 한가지 봐야할 부분은 UnaryOperator 와 BinayOperator 인터페이스는 compose 와 andThen 등의 디폴트 메소드를 가지고 있습니다. 해당 메소드를 통하여 다른 함수형 인터페이스를 만들수 있으니, 참고하세요.^^


1
2
3
4
5
6
7
UnaryOperator<String> unaryOperator = (String name) -> {
    return name;
};
Function<People, String> func = unaryOperator.compose((People people) -> people.getName());
 
People doohyun = obj.createObject(27"남두현");
System.out.println(func.apply(doohyun));
cs


Default method 로 등장장한 compose, andThen 에 대한 설명은 이 곳을 참고.



7. BiPredicate, BiConsumber, BiFunction

서로 다른 제네릭 형식 T, R 에 대한 비교를 수행할 수 있습니다. 

Bi가 붙은 인터페이스들은 서로 다른 제네릭에 대하여 수행을 합니다.


Bi 가 붙은 인터페이스는 함수디스크립터만 명시하겠습니다.


BiPredicate<L, R>   -->  (L, R)   --> boolean

BiConsumbe<T, U>   --> (T, U)   --> void

BiFunction<T, U, R>    --> (T, U)   --> R


1
2
3
4
5
6
7
@FunctionalInterface
public interface BiPredicate<T, R> {
    boolean test(T t, R r);
}
 
BiPredicate<Apple, String> biPredicate = (Apple apple, String type) -> type.equal(apple.getColor());
biPredicate.test(apple, "green");
cs



위와 같이, 이미 정의된 함수형 interface로 인해, 많은 기능을 사용할 수 있을 것 처럼 보입니다. 그러나 가만보니, 인터페이스에 붙어있는 제네릭 T에 의해 원사타입의 value 들(이를 테면 int, boolean 등)은 사용할 수 없는 것 처럼 보이네요.


그것에 대한 해결은 이 곳에서 확인 바랍니다.



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




반응형
Posted by N'